web-dev-qa-db-ja.com

ファイルをコピーして名前を変更します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.ファイルは最初に2つのディレクトリレベルアップでコピーされます。

/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で動作します)。

2
antoine

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"の場合:filename自体とそのサブディレクトリの2つのレベルを/samples/mydata1/RUN1/ID_date**/PCR2/TIME1**bold/*/*サフィックスに一致する部分は削除されます)、変数trgtに割り当てます。そのため、最初のファイルのtrgt/samples/mydata1/RUN1/ID_dateに設定されます。

"$1"は、現在の./filenameに対して相対的なファイルパス$PWDです。

${trgt##*/}_ "cut-up-to-last-prefix":trgt変数値を使用して、ファイル名の前に付けるサブディレクトリ名を取得しました。したがって、これはID_dateID2_date4、またはIDxxx_datexxxのみを返します。 、など(最後のスラッシュ/が表示されるまですべてを削除)、アンダースコア_を追加します。

この${1#./}は、相対的な./からポイントスラッシュ./filepathを削除します。

1
αғsнιη

これが私のソリューションの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 {} \;
1
igal

このバージョンでは、bashパラメーターの置換のみを使用してパスをスライスおよびダイスします。 1つ以上の絶対ファイルパスを渡します。

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

これが拡張バージョンです。これは相対パスも受け入れ、トラバースする親dirの数は調整可能です。

#!/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

特定のユースケースに関しては、「F3.bam」ファイルを見つける方法であれば、これをfindで実行できます。例えば:

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

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
0
ceving