上記の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. まず、ファイルを前の 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で動作)。

答え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パラメーターの置換のみを使用してパスをスライスしてサイコロします。 1 つ以上の絶対ファイルパスを渡します。

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

関連情報