ディレクトリにあるn個の大きなファイルを現在のディレクトリにコピーします。

ディレクトリにあるn個の大きなファイルを現在のディレクトリにコピーします。

ディレクトリ内の最大5つのファイルを自分のファイルにコピーしようとしていますpwdcp specific/directory$(ls -S specific/directory | head -n) ./最初のファイルをコピーしてcannot stat作成し続けると、リスト内の残りのファイルに対してエラーが発生します。

パイプラインが最初の項目では機能しますが、残りの項目では失敗するのはなぜですか?

答え1

ノート私のすべてのソリューションはファイルのみ、要求に応じて、あらゆる種類のファイルを処理できます。
(特殊文字も同様)

使いたいならls -S

正しい方法で実行してください。

ls --zero -S | head -z -n5 | xargs -r0 cp -t ./other/dir --

最近リクエストしましたGNU coreutils

coreutils 9.1-1ここで。

もう一つの方法はusebashおよび最近GNU findです。

findutils 4.9.0-4ここで。

に基づいてここ:

shopt -s nullglob
cd specific/directory/ || exit
print0 () { 
    [ "$#" -eq 0 ] || printf '%s\0' "$@"
}
readarray -td '' files < <(
    print0 * |
    find -files0-from - -maxdepth 0 -type f -printf '%b\t%p\0' |
    sort -rzn |
    cut -zf2 -
) 
cp -av -- "${files[@]:0:5}" "$OLDPWD"/
  • ${files[@]:0:5}最初の5つの要素に拡張文書0以上のキーを持つ配列。

以前のツールでは、Perlすべてのシェルを介して

perl -e 'rename($_, "./other/dir/$_") for ((sort { -s $b <=> -s $a } <*>))[0..4]'

答え2

以下を使用すると、zsh出力の解析と並べ替えに関連するすべてのトラップを回避できますls

cp -n -- specific/directory/*(.DOL[1,5]) ./

またはGNUを使用してくださいcp-tオプションの場合):

cp -n -t ./ -- specific/directory/*(.DOL[1,5])

どこグローバル予選はい

  • .通常のファイルのみが一致します(ディレクトリ、シンボリックリンク、fifo、ソケットは一致しません)。
  • Ddotglob オプションの切り替え - 隠しファイルを除外するには、このオプションを無視してください。
  • OL[1,5]ファイルの長さ(サイズ)で結果を並べ替え、上位5つを選択します。

この-nオプションは、cp名前の競合時に既存のファイルが破損するのを防ぎます。

答え3

他の回答の統合:


TL;博士:bashPOSIXシェルの可能な解決策については、以下を参照してください。


パイプラインが最初の項目では機能しますが、残りの項目では失敗するのはなぜですか?

シェルは、コマンドが想定するものとは異なる動作をするためです。

コマンド$(ls -S | head)の置き換えは実際にはその出力に置き換えられ、実際にはコードスニペットの右側に直接貼り付けられますcp specific/directory

  1. 二重引用符で囲まれていないため(これはエラーです)、コマンド置換の出力は変数に基づいてトークン化されますIFS。後者のデフォルトは、(単一スペース) + <tab> + <newline> 文字です。 <newline>はコマンドがls -S | head各ファイル名を区切るために使用するため、各名前はコマンドに従う独立した独立パスになりますcp。この場合、二重引用符コマンドの置き換えは役に立ちません。
  2. シェルはspecific/directory/名前のすべての部分もコピーしません(これはprop拡張の作業ですが、この場合は正しくインポートするのは難しいかもしれません)。したがって、最初の個々の名前だけがディレクトリプレフィックスを取得します。したがって、経由でアクセスできますcp。一方、他の4つの名前は現在のディレクトリにあると予想されていましたが、明らかにそうではありませんでした(しかし、実際にはターゲットcpディレクトリのファイルと同じファイルであるという不満があるでしょう)。./)

「動作」できますか?原則としてそうです。ただし、n個のファイルの1つに変数に指定された文字の1つが含まれると、競合が発生するため脆弱です。フルコントロールがないと、IFSさらに悪化します(1以下の説明を参照)。evalspecific/directory


bashPOSIXシェルに可能なソリューション

GNU coreutils v9.0以降を使用するときに使用できる他の回答に記載されているソリューションに加えて、シェルバリエーションを提供ls --zeroするGNU coreutils v8.25(2016年頃)以降を使用して安全に実行することもできます。そのためにls--quoting-style必要を使用してくださいeval。これはls、実際に機能するように設計されたこのオプションの利点を享受する唯一の方法です。そして eval

いつものevalように、特別な注意を払って処理する必要があります。ここではls、コマンドのみを使用し、文書化されlsた動作に基づいてシェルのファイル名を正しく引用することに依存します。追加の注意を払うには、/bin/ls偶然に存在するか、意図的に名前が付けられたことを知っているエクスポートされた悪意のある関数(またはエイリアス)を使用するリスクを取る代わりに、必要なオプションを提供する実行可能ファイルへの明示的なフルパスを呼び出すことができます。あります。ls--quoting-stylels$PATHls

したがってbash

(
  set -o pipefail \
    && o="$(/bin/ls -S --quoting-style=shell-escape-always | head -n 5)" \
    && eval "set -- $o" \
    && (("$#")) && cp -n -- "${@/#/specific/directory/}" .
)

に変更できますhead -n 5

上記のコードスニペットでは、安全性とエラーチェックを追加しましたが、実際にはすべてをデフォルトのコマンドに減らすことができます。もし誰ですか確かに肯定的ですあなたのlsバージョンに関して失敗したり間違った文字を出力したりする理由はありません。

(cd specific/directory && \
 eval "cp -n -- $(ls -S --quoting-style=shell-escape-always | head -n 5)"' "$OLDPWD"')

上記のPOSIXシェル用のソリューションと同等のソリューションも安全に機能します1。ただし、コマンドが提供するファイルの完全なリストをメモリにロードする必要があるため、完全に理想的ではありませんls。これらのリストはシェルに到達する前にフィルタリングできないため、ソースディレクトリには利用可能なメモリを埋めるのに十分なファイルが含まれてはいけません。それ以外の場合は、次のコマンドを実行する前にシェルが終了しますcp

(
  set -- && cd specific/directory \
    && o="$(/bin/ls -rSxw 0 --quoting-style=shell-always)" && eval "set -- $o" \
    && [ "$#" -gt 0 ] && n="$(($# - 5))" && shift "$(($n > 0 ? $n : 0))" \
    && cp -n -- "$@" "$OLDPWD"
)

ここでビットを変更して、上位n個のファイルの数を変更できます$(($# - 5))

このbashバージョンと同様に、必要な前提条件を再確認する限り、このバージョンも少し簡素化される可能性があります。合理化されたバージョンに加えて、bash少なくともn個のファイルが必要です。実際にそれ以外の場合、shiftコマンドは失敗し、シェルは早期に終了します(たとえば、ファイルが5つ未満の場合、この単純なバージョンspecific/directoryではそのファイルはコピーされません)。

(
  set -- && cd specific/directory \
    && eval "set -- $(ls -rSxw 0 --quoting-style=shell-always)" \
    && shift "$(($# - 5))" && cp -n -- "$@" "$OLDPWD"
)

1 メモ:簡略化と説明のために、上記の解決策はいいえファイルが実際に存在することを確認する定期的なファイルのみ(つまり、ディレクトリやシンボリックリンク、ソケット、名前付きfifo、デバイスファイルを除く)したがって、ソースディレクトリはするこのような「ファイル」が最初に最大のn個のファイルの中に存在する場合が発生します(有効数が0バイトであるにもかかわらず)。上記の解決策は〜する最後のコマンドcpにこれらの名前を含めます。これは特にシンボリックリンクとディレクトリに関連しています。いつも内容によっては個数が0より大きいため、ランクが高くなる場合があります。ls -S1つのレベルは廃棄されたファイルを置き換えます。ここで私のソリューションはすでにbash POSIXシェルの機能を拡張しているので、この状況を正しく処理するには、他の答えを参照してください。

答え4

編集:新しい答え、より完全な仕事:

ディレクトリ名が最初の結果にのみ追加されるため、ソースは失敗するため、現在のディレクトリに存在しない残りの結果は「ファイルなし」エラーが発生します。

望ましくない方法は、inode型を表す末尾の文字を含むオプションをfind使用することです。以下は、リストからディレクトリを削除する不完全な答えです。より完全な答えは、除外する必要がある他のinode型を削除します。このコマンドは実行可能ファイルに追加されました。-Flsgrepsed*-F

source="<some directory name>"
destination='.'
someCount=5 # e.g.
while IFS=\  read -r; do
    cp "${source}/${REPLY}" "${destination}"
done <<<"$(ls "${source}" -Ft | grep -v '/$' | head -5 | sed 's/\*$//')"

元の答え:

最大のファイルが1、2、3、4であるとしましょう。質問のコマンドは次のとおりです。

cp specific/directory/one two three four .

2、3、4がないため、コマンドは失敗します。似ている

source=specific/directory
set -f # disable globbing
IFS='
'      # split on newlines only
for file in $(ls -S $source); do
   cp "${source}/${file}" .
done

そうするでしょう。

警告:ファイル名に改行文字が含まれると中断されます(またはls端末に印刷しなくてもファイル名が破損しています)。

関連情報