ファイルのグループ(Filename *)をバックアップ(Filename * .bak)にコピーします


13

バックグラウンド

Linuxでは次のことができます。

  • ファイルのグループをリストします ls Filename*
  • でファイルのグループを削除します rm Filename*
  • ファイルのグループを移動する mv Filename* /New/Directory
  • ただし、次のようにファイルのグループをコピーすることはできませんcp Filename* *.bak

Linuxを変更する cpコマンドをファイルのグループをコピーする

名前を1つずつ入力せずにcpコマンドを使用せずにコピーしたいファイルのグループがあります。

$ ls gmail-meta3*
gmail-meta3                          gmail-meta3-REC-1558392194-26467821
gmail-meta3-LAB-1558392194-26467821  gmail-meta3-YAD-1558392194-26467821

古いDOSコマンドのようなものを使用するにはどうすればよいですか copy gmail-meta3* *.bakですか?

同様のコマンドを4回入力したくありません。

cp gmail-meta3-LAB-1558392194-26467821 gmail-meta3-LAB-1558392194-26467821.bak

私は、古いファイル名グループと新しいファイル名グループのパラメーターを受け入れ、ハードコードされたファイル名を持つものではないスクリプト/関数/アプリを探しています。たとえば、ユーザーは次のように入力できます。

copy gmail-meta3* *.bak

または、彼らはタイプするかもしれません:

copy gmail-meta3* save-*

1
この問題は、他のコマンドでは使用しないglob演算子を2回使用しているように見えます。bashはそれを処理するのに十分なほどスマートではありません。
qwr

2
@ qwr、bashはメタ文字を展開し、入力を実行するコマンドに渡す前にトークン化するという事実は、UNIXシェルの設計の一部です。どういうわけかcpコマンドの例外をプログラムしようとすると、bashの一貫性が完全に壊れてしまいます。練習として、ここでは何が起こっているかを試してみると数字、そしてなぜそれはので、それを作るシェルのメタキャラクタの展開です:touch aa ab ba; mkdir bb; cp a* b*; ls *
マイク・S

@MikeSポインターをありがとう。昨日*、ソースファイル名にはワイルドカードを使用できますが、ターゲットファイル名には使用できないと誰かが言いました。そのため、代替ワイルドカード(##提案されたと思いますが、私はに傾いています%)をターゲットに使用する必要があります。これはあなたが強化していることだと思いますか?cpコマンドを変更することはまったく期待していませんでした。copy(理由内で)DOSコピーコマンドをエミュレートするというラッパースクリプトを作成するだけです。
WinEunuuchs2Unix

@ WinEunuuchs2Unixその人は正しかった。シェルのメタキャラクターはコマンドに依存しません。したがって、すべてのワイルドカードは、パターンに一致するすべてのファイルを一致させようとします。汎用の「すべてを一致させ、この接尾辞を追加するものにコピーする」プログラムを作成しようとしている場合、はい、エスケープされていないメタキャラクターをターゲットとして配置しても、おそらく望みどおりにはなりません。そのため、すべてのシェルコマンドラインでのメタ文字が展開されています。ターゲットメタキャラクターが一致することは決してないと確信している場合は、シェルが展開できないため、それを使用できます。
マイクS

...しかし、それはやるべきいことです。特殊文字を使用することをお勧めします。%またはアンダースコアは適切で、通常はメタ文字ではありません(ただし、crontabファイルで%を使用する場合は注意してください。ここでは特別です)。
マイクS

回答:


14

以下は、sedこのタスクに適用可能なの1つの非典型的な使用例です。

sed -i.bak '' file-prefix*

この方法では、sedコマンドを提供しなかったため、実際にはファイルは変更されません''が、オプションにより-i[suffix]各ファイルのバックアップコピーが作成されます。検索中にこのアプローチを見つけました 。名前を2回入力せずに、ファイルのバックアップコピーを作成する方法はありますか。


$ time sed -i.bak '' gmail-meta3*real 0m0.069s
参考

ファイルが既に存在して、その後:real 0m0.037s。ファイルが削除され、cp速度に近い2回実行される場合:real 0m0.051s
WinEunuuchs2Unix

@xiota興味深い点。ターンアウトは、sedターゲット・ファイルがすでに存在する場合に高速ですが、cpターゲット・ファイルが存在する場合に遅くなります。sync大規模なタイミングテストを行うときではなく、実際にキャッシュとバッファをフラッシュしますが、今回はどちらも行いませんでした。これはまったく異なる質問トピックのメロドラマに脱線する可能性があるため、テスト結果を共有することを後悔しています:(この会話が起こったことのふりをするのは遅すぎますか?また、ハードディスクではなく、4チャネルのSamsung Pro 960 NVMe SSDです
WinEunuuchs2Unix

1
Linuxマシンでこれが発生している場合、これらのテスト用にどのストレージデバイスを使用するかはおそらく重要ではありません。カーネルは、メモリ内のファイルのバッファリングに非常に優れています。それが、freeコマンドを使用するときの「バフ/キャッシュ」値です。デバイスへの実際の書き込みは、キャッシュの経過時間とマシンのメモリ負荷を考慮したアルゴリズムによって選択された瞬間に行われます。複数のテストを試みる場合、最初のファイル読み取りはディスクから行われますが、その後の読み取りはほとんどの場合メモリから直接行われます(を参照sync; echo 3 > /proc/sys/vm/drop_caches)。
マイクS

昨日、2GBを超える大きなファイルでいくつかのテストを行いました-はい、このアプローチはcpコマンドを使用するよりも比較的遅いですが、パフォーマンスに大きな違いがあるとは言えません
pa4080

13

以下を使用できますfind

find . -max-depth 1 -name 'gmail-meta3*' -exec cp "{}" "{}.bak" \;

これにより、現在のディレクトリで.、globパターンに一致する名前を持つすべてのファイルが検索されます(シェルグロビングを防ぐために、パターンを単一引用符で囲むことに注意してください)。見つかったファイルごとにcp、nameからname.bakまで実行されます。\; 最後に、すべてのファイルを一度に渡すのではなく、各ファイルを個別に実行するようにします。最大の深さ1は、下に再帰するのではなく、cuurentディレクトリのみを検索します。


1
それはで動作しますfind . -max-depth 1 -name '"$1"'' -exec cp "{}" "{}$2" \;$ 1はソースと$ 2のときの拡張機能ですか?
WinEunuuchs2Unix

妥当であれば、$ 2で代用しても構いません。単一引用符内で変数置換を行うことはできないため、$ 1は扱いにくい場合があります。はっきりとはわかりませんが、パターンは文字列に格納されているため、二重引用符で$ 1を使用できる可能性があります。
cbojar

11

forループをbash使用できます。通常、これはワンライナーとして入力するだけです。これは頻繁に実行するタスクではないためです。

for f in test* ; do cp -a "$f" "prefix-${f}.ext" ; done

ただし、スクリプトとして必要な場合:

cps() {
   [ $# -lt 2 ] && echo "Usage: cps REGEXP FILES..." && return 1

   PATTERN="$1" ; shift

   for file in "$@" ; do
      file_dirname=`dirname "$file"`
      file_name=`basename "$file"`
      file_newname=`echo "$file_name" | sed "$PATTERN"`

      if [[ -f "$file" ]] && [[ ! -e "${file_dirname}/${file_newname}" ]] ; then
         cp -a "$file" "${file_dirname}/${file_newname}"
      else
         echo "Error: $file -> ${file_dirname}/${file_newname}"
      fi
   done
}

使い方はに似てい renameます。テストする:

pushd /tmp
mkdir tmp2
touch tmp2/test{001..100}     # create test files
ls tmp2
cps 's@^@prefix-@ ; s@$@.bak@' tmp2/test*    # create backups
cps 's@$@.bak@' tmp2/test*    # more backups ... will display errors
ls tmp2
\rm -r tmp2                   # cleanup
popd

$ time for f in gmail-meta3* ; do cp -a "$f" "${f}.bak" ; donereal 0m0.046s
参考

いいえ、時間を最適化したくありません。これは0.046、人間の知覚では0秒を意味する秒です。投稿された回答をテストし、sed上記のコマンドを見ていた視聴者に興味深い情報を伝える方法を示しようとしていました。あるいは、少なくとも私は比較に興味があったsedcp....
WinEunuuchs2Unix

ソリューションcpは、ソリューションよりも高速sedです。だからそれはお祝いの原因です:)
WinEunuuchs2Unix

(1)  -aは非標準のテストオペレーターです。なぜ使用しないの-eですか?(2)「一時ディレクトリを作成できません。」はやや誤解を招くエラーメッセージです。(3)なぜ単に使用しないのmktemp -dですか?(4)終了ステータスをテストする必要があります。たとえば、! mkdir "$FOLDER" && echo "Unable to create temporary directory." && return 1 またはと言う必要があり  mkdir "$FOLDER" || { echo "Unable to create temporary directory."; return 1;}ます。cpand についても  同様ですrenamepushd注意する場合は、場合によっては)…(続き)
G-Manは「Reinstate Monica」と言います

(続き)…(5)Arrggghhhh!言わないでください$@。と言う"$@"。(図5b)  そこには使用する必要はありません{と }したときに、変数にあなたがやっている方法を参照するには("${FOLDER}"、  "${PATTERN}" および  "${file}")。ただ"$FOLDER"、  "$PATTERN" そして  "$file"。(6)これは、ファイルが現在のディレクトリにあることを前提としています。  ではなく、現在のディレクトリにcps 's/$/.bak/' d/fooコピーd/fooします。foo.bak d/foo.bak
G-Manが「Reinstate Monica」と言う

6

DOSパラダイムに最も近いのはmcpmmvパッケージから)です:

mcp 'gmail-meta3*' 'gmail-meta3#1.bak'

zsh利用可能な場合、その貢献zmvモジュールはおそらく少し近いです:

autoload -U zmv

zmv -C '(gmail-meta3*)' '$1.bak'

私はls関係なく回避します - 空白(改行を含む)に対して安全なあなた自身の答えのバリアントは

printf '%s\0' gmail-meta3* | while IFS= read -r -d '' f; do cp -a -- "$f" "$f.bak"; done

多分

printf '%s\0' gmail-meta3* | xargs -0 -I{} cp -a -- {} {}.bak

mmvパッケージは理解していますが、コメントではコマンドはそうですmcpが、使用mmvするコマンドではmmvパッケージ内のコマンドでもあります。私はprintf例の方向が好きで、洗練されたスクリプトでは$ 1と$ 2が渡されたことを確認します。ボールを転がすための+1 :)
WinEunuuchs2Unix

@ WinEunuuchs2Unix謝罪-mcp / mmvは頭脳でした。実際にmcpは、同義語ですmmv -c
steeldriver

心配いりません。私が作ったすべてのタイプミスに1ドルあれば、私は億万長者になります:)私はprintf実際に使ったことがないコマンドの明確化を望みます。パラメーター1として渡されたprintf '%s\0' "$1"*場合gmail-meta3に機能すると言っていますか?
WinEunuuchs2Unix

@ WinEunuuchs2Unixおそらく呼び出し側のコンテキストにグロビングをさせてから、「$ @」cps gmail-meta3*と書きますprintf '%s\0。while ... `関数内。または、単に使用しますfor f; do cp -- "$f" "$f.bak"; donexiotaの答えと同様ですが、関数として)
steeldriver

1
zmv「ワイルドカード置換」モードを使用できることに注意してください。これは少しわかりやすいと思いますzmv -W -C 'gmail-meta3*' '*.bak'
。– 0x5453

5

rsyncのみのソリューション

ファイルを単にバックアップしたい場合は、それらを新しいディレクトリにコピーできます

rsync /path/to/dir/Filename* /path/to/backupdirectory

これにより、Filenameファイルがから/path/to/dir/にコピーされます/path/to/backupdirectory


rsync + filerename

バックアップファイルに接尾辞を付けたい場合は、ハッキングが発生しrsyncます...

rsync -Iu /path/to/dir/Filename* /path/to/dir/Filename* -b --backup-dir=/path/to/backupdirectory --suffix=.bak

これにより、既存のファイルが既存のファイル(-I)で上書きされますが、それらが-u新しい()場合(そうでない場合)にのみ、サフィックス付きのバックアップが作成されます。

同じディレクトリでそれを行うこともできます。ただし、既存のバックアップを除外する方が適切です。

rsync -Iu /path/to/dir/Filename* /path/to/dir/Filename* -b --backup-dir=/path/to/backupdirectory --suffix=.bak --exclude '*.bak'


私は大好きなrsycncので、支持しましたが、より簡単な方法はcp Filename* /path/to/backup/dir、ファイルが*.bak別のディレクトリにある場合、ファイルを一意化する必要がないからです。
WinEunuuchs2Unix

4

これは要求どおりに行う必要があります:

cps(){ p="${@: -1}"; for f in "${@:1:$#-1}"; do cp -ai "$f" "${p//\?/$f}"; done  }

使用法:

cps FILES... pattern
Example 1: cps gmail-meta3* ?.bak
Example 2: cps * save-?
Example 3: cps * bla-?-blubb

私が選んだ?ので#、それはそれ以外の場合は、コメントの開始として認識されますパターンの最初の文字、だ際に引用符で囲む必要があります。

テスト:

$ touch 'test};{bla#?"blubb'
$ cps test* bla-?-blubb
$ ls
test};{bla#?"blubb  bla-test};{bla#?"blubb-blubb


サフィックスを追加するためのスクリプトの以前のバージョン:

@ WinEunuuchs2Unixの回答に似ていますが、私はより柔軟で解析lsないと思います

cps(){ S="$1"; shift; printf '%s\0' "$@" | xargs -0 -I{} cp -abfS "$S" {} {}; }

これをあなたの.bashrc

使用法:

cps SUFFIX FILES...
Example: cps .bak gmail-meta3*

または、最後の引数として接尾辞を使用して(viaおよびvia):

cps(){ S="${@: -1}"; printf '%s\0' "${@:1:$#-1}" | xargs -0 -I{} cp -abfS "$S" {} {}; }

使用法:

cps FILES... SUFFIX
Example: cps gmail-meta3* .bak


素敵なコーディングですが、ソースターゲットを使用してコピーコマンドをターゲット、次にソース
ちょっと大変です-WinEunuuchs2Unix

後ろに接尾辞を付けて関数を追加しました。
pLumo

より直感的です。それをサフィックスと呼ぶことは、私の答えがコーディングした方法と同じですが、実際にはターゲットまたは宛先です。他のユーザーが使用したい場合がありますcopy gmail-meta3* old-meta3*。私の答えでは*、要求された私の質問のように宛先名に入る方法を理解できませんでした...
WinEunuuchs2Unix

問題は*シェルによって解釈されるため、関数はそれを認識しません。他の文字が必要か、引用符で囲んでから、関数内の元のファイル名に置き換えます。
pLumo

#代替ワイルドカードとして使用できると思い*ますか?だからあなたはタイプすることができますcopy filenames# save-#。ソースとターゲットでワイルドカード文字を同じにする必要があると思います。
WinEunuuchs2Unix

4

このワンライナーをに書きました~/.bashrc。を使用してより良い答えをfind投稿することができます。さらに良い答えをCで書くこともできます。うまくいけば、このQ&Aでより良い答えを得ることができます。

cps () {
    # cps "Copy Splat", copy group of files to backup, ie "cps Filename .bak"
    # Copies Filename1 to Filename1.bak, Filename2 to Filename2.bak, etc.
    # If Filename1.bak exists, don't copy it to Filename1.bak.bak
    for f in "$1"*; do [[ ! "$f" == *"$2" ]] && cp -a "$f" "$f$2"; done

    # OLD version comments suggested to remove 
    # ls "$1"* | while read varname; do cp -a "$varname" "$varname$2"; done
}
  • for f in "$1"*; do$1gmail-meta3パラメーターでfあり、一致するファイルのリストです。これを組み合わせて、Gmail-meta3、gmail-meta3-LAB-9999などの場合、以下を実行します。
  • [[ ! "$f" == *"$2" ]] &&:上記$fと同じfです。渡さ$2れる.bakパラメーターです。これを組み合わせると、ファイル名が末尾にない場合.bak(コピー.bakして作成したくないため.bak.bak)、次のようになります。
  • cp -a "$f" "$f$2"; gmail-meta3をgmail-meta3.bakなどにコピーします。
  • done:ループバックして、gmail-meta3*リストの次のファイル名を取得します。

cps gmail-meta3 .bak サンプル出力

ここで例として質問を使用すると、実際にどのように見えるかがわかります。

───────────────────────────────────────────────────────────────────────────────────────────
rick@alien:~/gmail$ ll gmail-meta3*
-rw-rw-r-- 1 rick rick 26467821 May 20 16:43 gmail-meta3
-rw-rw-r-- 1 rick rick 26467821 May 20 16:43 gmail-meta3.bak
-rw-rw-r-- 1 rick rick      643 May 20 16:43 gmail-meta3-LAB-1558392194-26467821
-rw-rw-r-- 1 rick rick      643 May 20 16:43 gmail-meta3-LAB-1558392194-26467821.bak
-rw-rw-r-- 1 rick rick    49607 May 20 16:44 gmail-meta3-REC-1558392194-26467821
-rw-rw-r-- 1 rick rick    49607 May 20 16:44 gmail-meta3-REC-1558392194-26467821.bak
-rw-rw-r-- 1 rick rick   728954 Jun 27 17:04 gmail-meta3-YAD-1558392194-26467821
-rw-rw-r-- 1 rick rick   728954 Jun 27 05:46 gmail-meta3-YAD-1558392194-26467821.bak
───────────────────────────────────────────────────────────────────────────────────────────
rick@alien:~/gmail$ cps gmail-meta3 .bak
───────────────────────────────────────────────────────────────────────────────────────────
rick@alien:~/gmail$ ll gmail-meta3*
-rw-rw-r-- 1 rick rick 26467821 May 20 16:43 gmail-meta3
-rw-rw-r-- 1 rick rick 26467821 May 20 16:43 gmail-meta3.bak
-rw-rw-r-- 1 rick rick      643 May 20 16:43 gmail-meta3-LAB-1558392194-26467821
-rw-rw-r-- 1 rick rick      643 May 20 16:43 gmail-meta3-LAB-1558392194-26467821.bak
-rw-rw-r-- 1 rick rick    49607 May 20 16:44 gmail-meta3-REC-1558392194-26467821
-rw-rw-r-- 1 rick rick    49607 May 20 16:44 gmail-meta3-REC-1558392194-26467821.bak
-rw-rw-r-- 1 rick rick   728954 Jun 27 17:04 gmail-meta3-YAD-1558392194-26467821
-rw-rw-r-- 1 rick rick   728954 Jun 27 17:04 gmail-meta3-YAD-1558392194-26467821.bak
───────────────────────────────────────────────────────────────────────────────────────────
rick@alien:~/gmail$ 

注:これは、コマンドで-aフラグを使用cpしてタイムスタンプを保持し、ファイルバックアップをよりよく把握できるようにします。

ファイルのコピーが元のものとまったく同じ日付と時刻を持っていることに注意してください。-aパラメーターが省略された場合、現在の日付と時刻が与えられ、ファイルサイズが同じであることを除いて、真のバックアップのようには見えません。


6
人々は常に解析を推奨しないls
-qwr

3
あなたが言及しているので、find私はあなたが構文解析の危険性を知っていると思いますlsか?しかし、あなたの場合はどちらも必要ありません:ただ行うfor file in "$1"*; do copy -a "$file" "$file$2"; done-これは完全に安全であり、lsorやfindand whileループを介したあらゆる種類の間接指定よりもはるかに簡単です。
コンラッドルドルフ

@KonradRudolphご提案ありがとうございます。いくつかの小さな変更を加えて、提案を実装およびテストしました。
WinEunuuchs2Unix

2

要件を達成する別の方法は、ファイルを一時ディレクトリにコピーし、renameコマンドを使用してそれらの名前を変更することです。

$ mkdir backup
$ cp filename* /tmp/rename-backup/
$ rename 's/(filename.*)/$1.bak/' /tmp/rename-backup/*
$ mv /tmp/rename-backup/* ./

スクリプトとして必要な場合は、次のように使用できます

cps () {
    mkdir -p /tmp/rename-backup/
    cp "$1"* /tmp/rename-backup/
    rename "s/($1.*)/\$1.$2/" /tmp/rename-backup/*
    mv "/tmp/rename-backup/$1"*".$2" .
}

そして、次のように使用できます。

cps file bak

これは一例です

$ ls -l
total 0
-rw-r--r--  1 danny  danny  0 Jun 26 16:23 file a
-rw-r--r--  1 danny  danny  0 Jun 26 16:23 file ab
-rw-r--r--  1 danny  danny  0 Jun 26 16:23 file ac
-rw-r--r--  1 danny  danny  0 Jun 26 16:05 filename1
-rw-r--r--  1 danny  danny  0 Jun 26 16:05 filename2
-rw-r--r--  1 danny  danny  0 Jun 26 16:05 filename3
-rw-r--r--  1 danny  danny  0 Jun 26 16:05 filename4
$ cps file bak
$ ls -l
total 0
-rw-r--r--  1 danny  danny  0 Jun 26 16:23 file a
-rw-r--r--  1 danny  danny  0 Jun 26 16:41 file a.bak
-rw-r--r--  1 danny  danny  0 Jun 26 16:23 file ab
-rw-r--r--  1 danny  danny  0 Jun 26 16:41 file ab.bak
-rw-r--r--  1 danny  danny  0 Jun 26 16:23 file ac
-rw-r--r--  1 danny  danny  0 Jun 26 16:41 file ac.bak
-rw-r--r--  1 danny  danny  0 Jun 26 16:05 filename1
-rw-r--r--  1 danny  danny  0 Jun 26 16:41 filename1.bak
-rw-r--r--  1 danny  danny  0 Jun 26 16:05 filename2
-rw-r--r--  1 danny  danny  0 Jun 26 16:41 filename2.bak
-rw-r--r--  1 danny  danny  0 Jun 26 16:05 filename3
-rw-r--r--  1 danny  wheel  0 Jun 26 16:41 filename3.bak
-rw-r--r--  1 danny  danny  0 Jun 26 16:05 filename4
-rw-r--r--  1 danny  danny  0 Jun 26 16:41 filename4.bak
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.