"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 버전은 다음과 같습니다. 프로세스 대체와 함께 dirname
및 basename
명령을 사용하여 복사 명령에 대한 대상 경로를 구축할 수 있습니다.
더 자세한 설명은 다음과 같습니다.
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
사용 find
및shell (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