위의 2개 디렉터리에 있는 파일을 복사하고 이름을 바꿉니다.

위의 2개 디렉터리에 있는 파일을 복사하고 이름을 바꿉니다.

"F3.bam"이라는 이름의 여러 파일을 2단계 디렉터리에 복사한 다음 복사한 후 하위 디렉터리 이름으로 파일 이름을 바꾸려고 합니다.

예를 들어:

/samples/mydata1/RUN1/ID_date/PCR2/TIME1/F3.bam
/samples/mydata2/RUN1/ID2_date4/PCR2/TIME7/F3.bam
/samples/mydataxxx/RUN1/IDxxx_datexxx/PCR2/TIMExxx/F3.bam

예상 결과:

1. 먼저 파일을 이전 두 디렉터리에 복사합니다.

/samples/mydata1/RUN1/ID_date/F3.bam
/samples/mydata2/RUN1/ID2_date4/F3.bam
/samples/mydataxxx/RUN1/IDxxx_datexxx/F3.bam

2. 현재 하위 디렉터리의 이름에 따라 파일 이름을 바꿉니다.

/samples/mydata1/RUN1/ID_date/ID_date_F3.bam
/samples/mydata2/RUN1/ID2_date4/ID2_date4_F3.bam
/samples/mydataxxx/RUN1/IDxxx_datexxx/IDxxx_datexxx_F3.bam

이상적으로는 bash 루프가 좋습니다(Mac에서 작동).

답변1

내 솔루션의 TLDR 버전은 다음과 같습니다. 프로세스 대체와 함께 dirnamebasename명령을 사용하여 복사 명령에 대한 대상 경로를 구축할 수 있습니다.

더 자세한 설명은 다음과 같습니다.


Bash 루프를 사용하여 대략적으로 원하는 작업을 수행하는 (매우 상세한) 스크립트는 다음과 같습니다.

#!/bin/bash

# copy_and_rename.bash
#
#   Copy multiple files 2 folders up and rename these files
#   to contain their parent directory as a prefix.
#

# Set internal field separator to handle spaces in file names
IFS=$'\n'

# Iterate over the list of file paths
for _file_path in $@; do

    # Get the file name
    _file_name="$(basename ${_file_path})"

    echo "${_file_name}"

    # Get the path to the target directory (two levels above the file)
    _target_directory_path=$(dirname $(dirname ${_file_path}))

    echo "${_target_directory_path}"

    # Get the parent directory of the target directory
    _parent_directory_path=$(dirname ${_target_directory_path})

    echo "${_parent_directory_path}"

    # Get the name of the parent directory
    _parent_directory_name=$(basename ${_parent_directory_path})

    echo "${_parent_directory_name}"

    # Construct the new file path
    _new_file_path="${_target_directory_path}/${_parent_directory_name}_${_file_name}"

    echo "${_new_file_path}"

    # Copy and rename the file
    echo "cp -i \"${_file_path}\" \"${_new_file_path}\""
    cp -i "${_file_path}" "${_new_file_path}"
    echo
done

물론 많이 압축할 수도 있지만 값을 설명하기 위해 이렇게 유지했습니다.

echo주석이나 관련 없는 변수 또는 명령문 없이 앞의 스크립트는 다음과 같습니다 .

for _file_path in $@; do
    cp -i "${_file_path}" \
    "$(dirname $(dirname ${_file_path}))/$(basename $(dirname $(dirname $(dirname ${_file_path}))))_$(basename ${_file_path})"
done

매우 부서지기 쉽고 오류 처리 측면에서 많은 작업을 수행하지 않습니다. 또한 디버깅을 위한 몇 가지 명령문을 남겨두었 echo기 때문에 처음 실행할 때 디버깅이 수행되는 작업을 확인하고 제대로 작동하는지 확인할 수 있습니다.

테스트하기 위해 다음 스크립트를 사용하여 파일을 만들었습니다. 추가 테스트에 유용할 경우를 대비해 여기에 포함시켰습니다.

#!/bin/bash

# create_test_files.bash

# Set internal field separator to handle spaces in file names
IFS=$'\n'

# Choose an prefix for the file paths
_prefix="/tmp"

# Create array of sample files
_sample_files=(
    "/samples/mydata1/RUN1/ID_date/PCR2/TIME1/F3.bam"
    "/samples/mydata2/RUN1/ID2_date4/PCR2/TIME7/F3.bam"
    "/samples/mydataxxx/RUN1/IDxxx_datexxx/PCR2/TIMExxx/F3.bam"
)

# Create directories and files
for _file in "${_sample_files[@]}"; do

    # Add the prefix to the path
    _path="${_prefix}${_file}"

    # Create parent directory
    mkdir -p "$(dirname ${_path})"

    # Create file
    touch "${_path}"
done

다음 명령을 사용하여 파일이 올바르게 생성되었는지 확인했습니다 find.

$ find /tmp/samples -type f

/tmp/samples/mydata1/RUN1/ID_date/PCR2/TIME1/F3.bam
/tmp/samples/mydata2/RUN1/ID2_date4/PCR2/TIME7/F3.bam
/tmp/samples/mydataxxx/RUN1/IDxxx_datexxx/PCR2/TIMExxx/F3.bam

그런 다음 스크립트를 다음과 같이 호출합니다.

bash copy_and_rename.bash \
/tmp/samples/mydata1/RUN1/ID_date/PCR2/TIME1/F3.bam \
/tmp/samples/mydata2/RUN1/ID2_date4/PCR2/TIME7/F3.bam \
/tmp/samples/mydataxxx/RUN1/IDxxx_datexxx/PCR2/TIMExxx/F3.bam

그런 다음 다음을 사용하여 스크립트가 다시 작동하는지 확인합니다 find.

$ find /tmp/samples -type f

/tmp/samples/mydata1/RUN1/ID_date/PCR2/ID_date_F3.bam
/tmp/samples/mydata1/RUN1/ID_date/PCR2/TIME1/F3.bam
/tmp/samples/mydata2/RUN1/ID2_date4/PCR2/ID2_date4_F3.bam
/tmp/samples/mydata2/RUN1/ID2_date4/PCR2/TIME7/F3.bam
/tmp/samples/mydataxxx/RUN1/IDxxx_datexxx/PCR2/IDxxx_datexxx_F3.bam
/tmp/samples/mydataxxx/RUN1/IDxxx_datexxx/PCR2/TIMExxx/F3.bam

마지막으로 다음을 사용하여 모든 테스트 파일을 제거했습니다 find.

find /tmp/samples -type f -exec rm {} \;

답변2

이 버전은 bash 매개변수 대체만 사용하여 경로를 슬라이스하고 주사위합니다. 하나 이상의 절대 파일 경로를 전달합니다.

#!/bin/env bash
for path; do
    dir="${path%/*}"
    dest="${dir%/*/*}"
    cp "$path" "${dest}/${dest##*/}_${path##*/}"
done

이것은 확장 버전입니다. 이는 상대 경로도 허용하며 통과할 상위 디렉터리 수는 조정 가능합니다.

#!/bin/env bash

# Each param for this script is the path of a file. It
# accepts relative paths if you have appropriate tool to
# robustly determine absolute paths (not trivial). Here
# we're using GNU 'realpath' tool.
#
# Usage: copy2up filepath1 [filepath2...]

# for converting relative paths to absolute
# if it's missing replace realpath with available tool
# (or just always use absolute path arguments)
pathtool=realpath

# directory levels upwards to copy files
levels=2

# iterate over each parameter
for path; do
    if [[ ! $path =~ ^/ ]]; then
        # convert relative to absolute
        path="$($pathtool $path)"
    fi
    file="${path##*/}"
    dir="${path%/*}"

    dest=$dir
    # chdir upwards 'levels' times to destination
    for (( i=0; i<$levels; i++ )); do
        dest="${dest%/*}"
    done

    # to be prepended to original filename
    destpfx="${dest##*/}"

    newpath="${dest}/${destpfx}_${file}"
    cp "$path" "$newpath"
done

특정 사용 사례에서 find"F3.bam" 파일을 찾는 방법이라면 이 명령을 실행할 수 있습니다. 예를 들어:

find /some/path -name F3.bam -exec copy2up.sh {} +

답변3

사용 findshell (POSIX sh/bash/Korn/zsh) parameter substitution expansion다음과 같이.

find . -type f -name "F3.bam" -execdir sh -c '
    trgt="${PWD%/*/*}"; echo cp -v "$1" "${trgt}/${trgt##*/}_${1#./}" ' _ '{}' \;

설명하다::

F3.bam-execdir여기서만 일치하는 파일을 찾고 있습니다 . 즉, 현재 디렉터리를 파일이 있는 디렉터리로 변경한 다음 find해당 디렉터리 자체 내에서 F3.bam실행합니다 .sh -c ' ... '

trgt="${PWD%/*/*}""cut-up-to-first-suffix" 사용 : 우리는 타고 있습니다.파일 이름자체 및 2단계 하위 디렉터리 /samples/mydata1/RUN1/ID_date**/PCR2/TIME1**(용감한접미사와 일치하는 부분은 /*/*제거되고 변수에 할당됩니다 trgt. 이제 첫 번째 파일로 trgt설정되었습니다 ./samples/mydata1/RUN1/ID_date

"$1"상대적인 거야파일 경로 ./filename지금까지 $PWD.

${trgt##*/}_"cut-up-to-last-prefix": 변수 값을 사용하여 파일 trgt이름 앞에 배치되어야 하는 하위 디렉터리 이름을 가져오므로 이는 ID_date, ID2_date4또는 IDxxx_datexxx등을 반환합니다(마지막 이름이 보일 때까지 모든 것을 제거합니다). 슬래시 바 /) 및 밑줄을 추가합니다 _.

이렇게 하면 상대 항목에서 ${1#./}점 슬래시가 제거됩니다 .././filepath

답변4

dirname원하는 만큼 여러 번 중첩할 수 있습니다.

set /samples/mydata1/RUN1/ID_date/PCR2/TIME1/F3.bam \
/samples/mydata2/RUN1/ID2_date4/PCR2/TIME7/F3.bam \
/samples/mydataxxx/RUN1/IDxxx_datexxx/PCR2/TIMExxx/F3.bam

for bam; do
  dir="$(dirname "$(dirname "$(dirname "$bam")")")"
  mv "$bam" "$dir"/"$(basename "$dir")"_"$(basename "$bam")"
done

관련 정보