headの出力を使用して、空白のあるファイルをコピーします。

headの出力を使用して、空白のあるファイルをコピーします。

私のフォルダには名前にスペースを含む11個のファイルがありますが、最新の10個のファイルをコピーしたいと思います。以前は、ls -t | head -n 10最新の10個のファイルしかインポートできませんでした。 cpステートメントで式を使用しようとすると、名前にスペースが含まれているため、ファイルが見つからないというエラーが発生します。

たとえば、次のようにcp: cannot stat ‘10’: No such file or directoryなります。10 abc.pdf

CPドア:cp $(ls -t | head -n 10) ~/...

どのように動作させることができますか?

答え1

Bashを使用していて100%安全な方法が必要な場合(ファイル名に注意する必要がある難しい方法を学んだので、あなたも欲しいと思います)、次のことができます。

shopt -s nullglob
while IFS= read -r file; do
    file=${file#* }
    eval "file=$file"
    cp "$file" Destination
done < <(
    for f in *; do
        printf '%s %q\n' "$(date -r "$f" +'%s')" "$f"
    done | sort -rn | head -n10
)

そのうちのいくつかを見てみましょう。

for f in *; do
    printf '%s %q\n' "$(date -r "$f" +'%s')" "$f"
done

これは、フォームの標準出力用語で印刷されます。

Timestamp Filename

ここで、タイムスタンプはエポック(-rオプションで得られたdate)以降の変更日(秒)で、ファイル名は引用されたファイル名のバージョンです。シェル入力で再利用可能(フォーマット指定子を参照してくださいhelp printf%q。次に、行を数字順、逆順に並べ替え、sort最初の10行だけを保持します。

while次にループに供給します。タイムスタンプは割り当てfile=${file# *}(最初のスペースの前のすべてのエントリを削除)として削除され、明らかに危険な行にはeval "file=$file"エスケープ文字が削除され、printf %q最後にファイルを安全にコピーできます。

おそらく最善のアプローチや実装ではないかもしれませんが、可能なすべてのファイル名に対して100%の安全性が保証され、作業が完了します。ただし、これは通常のファイル、ディレクトリなどを同じように扱います。通常のファイルに制限するには、[[ -f $f ]] || continueこのfor f in *; do行の後に追加してください。また、隠しファイルは考慮しません。ファイルを隠すには(もちろんそうでは.ありません..shopt -s dotglob


もう1つの100%Bashソリューションは、Bashを直接使用してファイルをソートすることです。クイックソートの使用方法は次のとおりです。

quicksort() {
    # quicksorts the filenames passed as positional parameters
    # wrt modification time, newest first
    # return array is quicksort_ret
    if (($#==0)); then
        quicksort_ret=()
        return
    fi
    local pivot=$1 oldest=() newest=() f
    shift
    for f; do
        if [[ $f -nt $pivot ]]; then
            newest+=( "$f" )
        else
            oldest+=( "$f" )
        fi
    done
    quicksort "${oldest[@]}"
    oldest=( "${quicksort_ret[@]}" )
    quicksort "${newest[@]}"
    quicksort_ret+=( "$pivot" "${oldest[@]}" )
}

その後、それらをソートして上位10個を保持してからターゲットにコピーします。

$ shopt -s nullglob
$ quicksort *
$ cp -- "${quicksort_ret[@]:0:10}" Destination

以前の方法と同様に、通常のファイル、ディレクトリなどを同じように処理し、隠しファイルをスキップします。


別のアプローチ:およびパラメータがlsある場合は、次のものを使用できます。iq

ls -tiq | head -n10 | cut -d ' ' -f1 | xargs -I X find -inum X -exec cp {} Destination \; -quit

これにより、ファイルの inode が表示され、findその inode が参照するファイルからコマンドを実行できます。

繰り返しますが、これは通常のファイルだけでなくディレクトリなども処理します。ls出力形式に依存しすぎるので、これはあまり好きではありません...

答え2

ディレクトリ内のすべてのファイルグループに対して、常に最も古いファイルを無視します。

less_oldest() {
    while [ -e "$1" ] ; do
        for f do [ "$f" -ot "$1" ] && break
        done && { set -- "$@" "$1"
            shift ; continue
        } ; shift ; break
    done
    printf '///%s///\n' "$@" |
        sed "s|'"'|&"&"&|g;'"s|///|'|g"
}

これは完全に移植可能で、返すことができる文字に関係なく、シェル安全ファイル名の配列を印刷します。あなたの場合はdro poneだけが必要なので、次のように一度だけ実行してください。

{   printf 'cp -t ~"/..."'
    less_oldest "${dir_of_11_files}/"*
} | sh

関連情報