Dosya grubunu (Dosya adı *) yedeğe kopyala (Dosya adı * .bak)


13

Arka fon

Linux'ta şunları yapabilirsiniz:

  • İle bir grup dosyayı listele ls Filename*
  • İle bir grup dosyayı kaldırma rm Filename*
  • İle bir grup dosyayı taşıma mv Filename* /New/Directory
  • Ancak aşağıdakileri içeren bir grup dosyayı kopyalayamazsınız:cp Filename* *.bak

cpDosya grubunu kopyalamak için Linux komutunu değiştirme

İsimleri tek tek girmeden ve cpkomutu kullanarak kopyalamak istediğim bir dosya grubum var :

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

Eski DOS komutu gibi bir şeyi nasıl kullanabilirim copy gmail-meta3* *.bak?

Benzer komutu dört kez yazmak istemiyorum:

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

Sabit kodlu dosya adları ile bir şey değil, eski ve yeni dosya adı grubu için parametreleri kabul eden bir komut dosyası / işlev / uygulama arıyorum. Örneğin, bir kullanıcı şunları yazabilir:

copy gmail-meta3* *.bak

veya şunu yazabilirler:

copy gmail-meta3* save-*

1
Sorun bana diğer komutlarınızın hiçbirini kullanmayan iki kez glob operatörü kullanarak bakıyor. bash üstesinden gelebilecek kadar akıllı değil.
qwr

2
@qwr, bash'ın metakarakterleri genişletmesi ve girişi yürütülecek bir komuta teslim etmeden önce girişi tokenleştirmesi, UNIX mermilerinin tasarımının bir parçasıdır. Bir şekilde cp komutu için bir istisna denemek ve programlamak, hiç akıllı olmayacak olan bash'ın tüm tutarlılığını kıracaktır. Bir egzersiz olarak, burada neler olduğunu ve bunu neden kabuğun metakarakter genişletmesi olduğunu anlamaya çalışın:touch aa ab ba; mkdir bb; cp a* b*; ls *
Mike S

@MikeS İşaretçiler için teşekkürler. Dün birisi *kaynak dosya adları için joker karakter kullanabileceğinizi , ancak hedef dosya adları için kullanamayacağınızı söyledi . Gibi yerine joker ( ##önerilen olduğunu düşünüyorum ama doğru eğilmiş %) hedef için kullanılması gerekecektir. Sanırım bunu güçlendiriyorsun? cpKomutu hiç değiştirmeyi beklemiyordum . copyDOS kopyalama komutunu taklit eden (bir sebeple) adlı bir sarıcı komut dosyası oluşturun .
WinEunuuchs2Unix

@ WinEunuuchs2Unix doğru kişiyi işaretleyin. Kabuk metakarakterleri komutlardan bağımsızdır. Böylece tüm joker karakterler, kalıpla eşleşen tüm dosyaları eşleştirmeye çalışır. Genel amaçlı bir "her şeyi eşleştirin ve ne olursa olsun kopyalayın ancak bu soneki ekleyin" programını yapmaya çalışıyorsanız, evet, hedef olarak kaçışsız bir metakarakteriz muhtemelen istediğinizi yapmayacaktır. Çünkü kabuk komut satırındaki tüm metakarakterler genişletilir. Hedef metakarakterlerin hiçbir zaman bir eşleşme oluşturmayacağından emin olursanız, bunu kullanabilirsiniz - çünkü kabuk onu genişletemez.
Mike S

... ama bu çirkin bir şey olurdu. Özel bir karakter kullanmak daha iyidir. % veya alt çizgi iyidir, genellikle meta karakter değildir (ancak bir crontab dosyasında% kullanmaya dikkat edin; özeldir).
Mike S

Yanıtlar:


14

Aşağıda, sedbu görev için geçerli olan atipik bir kullanım örneği verilmiştir :

sed -i.bak '' file-prefix*

Bu şekilde, aslında sedherhangi bir komut sağlamadığımız için dosyaları değiştirmeyecek '', ancak seçenek nedeniyle -i[suffix]her dosyanın yedek bir kopyasını oluşturacaktır. Arama yaparken bu yaklaşımı buldum Dosya adını iki kez yazmadan yedek kopyasını oluşturmanın herhangi bir yolu var mı?


FYI: $ time sed -i.bak '' gmail-meta3*=real 0m0.069s
WinEunuuchs2Unix

Dosyalar varsa zaten o zaman mevcut: real 0m0.037s. Dosyalar silinmiş ve bir ikinci kez kapanış çalıştırırsanız cphızı: real 0m0.051s.
WinEunuuchs2Unix

@xiota İlginç bir nokta. Dönüşler dışarı sedhedef dosyalar zaten var olduğunda daha hızlıdır ancak cphedef dosyalar bulunduğunda yavaştır. Aslında syncbüyük zamanlama testleri yapmak yerine önbellekleri ve tamponları temizlerim ama bu sefer de yapmadım. Bu tamamen farklı bir konu dışı sabun operasına girebildiğim için test sonuçlarımı paylaştığım için pişmanım :( Bu konuşma hiç olmamış gibi davranmak için çok mu geç? FYI gmail mesajı meta veri boyutu 2.5 MB ve 3 dizin dosyası yaklaşık 800 KB Ayrıca sabit disk değil, 4 kanalda bir Samsung Pro 960 NVMe SSD
WinEunuuchs2Unix

1
Bir Linux makinesinde gerçekleşiyorsa, muhtemelen bu testler için hangi depolama cihazına sahip olduğunuz önemli değildir. Çekirdek bellekteki dosyaları arabelleğe almada son derece iyidir. freeKomutu kullanırken "buff / cache" değeri budur . Cihaza gerçek yazma işlemleri, önbellek yaşını ve makinedeki bellek basıncını dikkate alan bir algoritma tarafından seçilen bir anda gerçekleşir. Birden fazla test yapmayı deniyorsanız, ilk dosya okumaları diskten gelir, ancak sonraki okumalar büyük olasılıkla doğrudan bellek dışına çıkar (bkz. sync; echo 3 > /proc/sys/vm/drop_caches).
Mike S

Dün 2GB üzerinde büyük dosyalarla birkaç test yaptım ve - evet, bu yaklaşım cpkomutun kullanımından nispeten yavaş , ancak önemli bir performans farkı olduğunu söyleyemem .
pa4080

13

Şunları kullanabilirsiniz find:

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

Bu, geçerli dizinde .glob deseniyle eşleşen bir ada sahip tüm dosyaları bulacaktır (kabuk globbingini önlemek için desen etrafındaki tek tırnaklara dikkat edin). Bulunan her dosya cpiçin, addan name.bak dosyasına yürütülür. \; Sonunda, hepsini bir kerede geçirmek yerine her dosyayı ayrı ayrı yapmasını sağlar. 1 olarak maksimum derinlik, tekrarlayan yerine yalnızca cuurent dizininde arama yapar.


1
find . -max-depth 1 -name '"$1"'' -exec cp "{}" "{}$2" \;$ 1 kaynak ve $ 2 uzantı olduğunda işe yarar mı?
WinEunuuchs2Unix

2 $ makul olduğu sürece yerine Tamam olmalıdır. Tek tırnak içinde değişken ikame yapamadığımız için 1 $ daha zor olabilir. Ben hazırlıksız emin değilim ama desen bir dize saklandığı için $ 1 çift tırnak kullanmak mümkün olabilir.
cbojar

11

İle bir fordöngübash kullanabilirsiniz . Normalde, sadece tek katmanlı olarak yazardım çünkü bu sık sık yaptığım bir görev değil:

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

Ancak, komut dosyası olarak ihtiyacınız varsa:

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
}

Kullanımı benzer rename. Test etmek için:

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

FYI: $ time for f in gmail-meta3* ; do cp -a "$f" "${f}.bak" ; done=real 0m0.046s
WinEunuuchs2Unix

Um hayır zaman için optimize etmek istemiyorum. Bu 0.046saniyedir, yani insan algısı için 0 saniyedir. Sadece gönderilen cevapları test ettiğimi ve sedyukarıdaki komuta bakan izleyicilere ilginç haberleri nasıl aktardığımı göstermeye çalışıyordum . Ya da en azından ben karşılaştırarak ilgilenen edildi sediçin cp....
WinEunuuchs2Unix

Çözümünüz, çözümünüzden cpdaha hızlıdır sed. Bu kutlama için bir neden :)
WinEunuuchs2Unix

(1)  -astandart olmayan bir test operatörüdür. Neden kullanmıyorsunuz -e? (2) “Geçici dizin oluşturulamıyor.” biraz yanıltıcı bir hata mesajıdır. (3) Neden sadece kullanmıyorsunuz mktemp -d? (4) Çıkış durumlarını test etmelisiniz. Örneğin, ! mkdir "$FOLDER" && echo "Unable to create temporary directory." && return 1 veya  demelisiniz mkdir "$FOLDER" || { echo "Unable to create temporary directory."; return 1;}. Aynı şekilde cpve  rename(ve belki pushdde dikkatli olmak istiyorsanız). … (Devam ediyor)
G-Man

(Devamı)… (5) Arrggghhhh! Sakın söyleme $@; demek "$@". (5b) Kullanmanıza  gerek yoktur {ve } değişkenlere yaptığınız gibi başvurduğunuzda ( "${FOLDER}",  "${PATTERN}" ve  "${file}"); sadece bunu "$FOLDER",  "$PATTERN" ve  "$file". (6) Bu, dosyaların geçerli dizinde olduğunu varsayar.  cps 's/$/.bak/' d/fookopyalar d/fooiçin foo.bak , geçerli dizinde değil d/foo.bak.
G-Man, 'Monica'yı Yeniden Başlat' diyor

6

DOS paradigmasına en yakın alacağınız paketten mcpitibaren mmv:

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

Varsa zsh, katkıda bulunan zmvmodülü belki de biraz daha yakındır:

autoload -U zmv

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

Ne lsolursa olsun kaçınacağım - kendi cevabınızdaki boşluk (yeni satırlar dahil) için güvenli olan bir varyant

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

ya da belki

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

Anlıyorum mmvpaket ama yorumlarda komut olduğunu söyleyin mcpama daha sonra kullandığınız mmvkomutta da hangi mmvpaket içinde bir komut . printfÖrneklerin yönünü seviyorum ve cilalı bir senaryoda $ 1 ve $ 2'nin geçmesini sağlarım. Top haddeleme için +1 :)
WinEunuuchs2Unix

@ WinEunuuchs2Unix özür - mcp / mmv bir beyin kanadıydı. Aslında mcpsadece bir eşanlamlımmv -c
steeldriver

Pfft endişelenme. Yaptığım her yazım hatası için bir dolarım olsaydı ben milyoner olurdum :) printfHiç kullanmadığım komutlara açıklama yapmak istiyorum . Parametre 1 olarak geçirilirse printf '%s\0' "$1"*işe yarayacağını mı söylüyorsunuz gmail-meta3?
WinEunuuchs2Unix

@ WinEunuuchs2Unix Muhtemelen çağıran bağlamın zonklama yapmasına izin veriyorum cps gmail-meta3*ve sonra printf '%s\0"$ @" yazıyorum | süre ... `` işlevde. Veya sadece kullanın for f; do cp -- "$f" "$f.bak"; done( xiota'nın yanıtı gibi , ancak bir işlev olarak)
steeldriver

1
zmvSeninle biraz daha kolay bulmak "joker karakter değiştirme" modunu kullanabilirsiniz unutmayın :zmv -W -C 'gmail-meta3*' '*.bak'
0x5453

5

sadece rsync çözümü

Yalnızca dosyalarınızı yedeklemek istiyorsanız, yeni bir dizine kopyalayabilirsiniz

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

Bu kopyalar Filenamedosyaları /path/to/dir/için /path/to/backupdirectory.


rsync + dosya adı

Yedek dosyalarınızın bir sonek olmasını istiyorsanız, işler hacky olur rsync...

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

Bu, mevcut dosyaları ile ... varolan dosyaların üzerine olacaktır ( -I) ancak yalnızca, ( -u) (onlar değil ki) daha yeni ve bir sonek ile yedeğini oluşturma.

Bunu aynı dizinde de yapabilirsiniz. Ancak mevcut yedeklemeleri daha iyi hariç tutun.

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


Ben rsycncbu yüzden upvoted seviyorum , ancak daha basit bir yöntem, cp Filename* /path/to/backup/dirdosyaları *.bakayrı bir dizinde olsaydı benzersiz benzersiz gerekmez çünkü olurdu .
WinEunuuchs2Unix

4

Bu, istendiği gibi yapmalıdır:

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

Kullanımı:

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

Seçtim ?çünkü #desenin ilk karakteri olduğunda alıntılanmalıdır, aksi takdirde bir yorumun başlangıcı olarak kabul edilir.

Ölçek:

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


Sonek eklemek için komut dosyasının önceki bazı sürümleri:

@ WinEunuuchs2Unix yanıtına benzer, ancak daha esnek ve ayrıştırmadığınıls düşünüyorum :

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

Bunu içine koy .bashrc.

Kullanımı:

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

Alternatif, son argüman olan sonekle ( üzerinden ve yoluyla ):

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

Kullanımı:

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


Güzel kodlama ama on yıl sonra Kaynak sonra Hedef komut sonra Hedef sonra Kaynak
WinEunuuchs2Unix 26:19

Soneki arkada olan fonksiyon eklendi.
pLumo

Teşekkürler bu daha sezgisel. Bir sonek olarak adlandırmak, cevabımın kodlama şekli doğrudur, ancak gerçekten bir hedef veya hedeftir. Diğer kullanıcılar kullanmak isteyebilirsiniz: copy gmail-meta3* old-meta3*. *
Cevabımda

Sorun, *kabuk tarafından yorumlanmasıdır, bu nedenle işlev bunu bilmeyecektir. Başka bir karaktere ihtiyacınız olacak veya alıntı yapacaksınız, daha sonra onu işlev içindeki orijinal dosya adıyla değiştirin.
pLumo

#Yedek bir joker olarak kullanılabilir sanırım *? Böylece yazabilirsiniz copy filenames# save-#. Sanırım joker karakterin kaynak ve hedef için aynı olmasını istersiniz.
WinEunuuchs2Unix

4

Ben bu tek astarı yazdım ~/.bashrc. findSanırım çok daha iyi cevaplar kullanarak gönderilebilir. Daha iyi cevaplar C'de yazılabilir. Umarım bu soru-cevap daha iyi cevaplar için topu yuvarlar:

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: $1Olduğu gmail-meta3parametre ve fdosyaları eşleme listesi aşağıdadır. Bu, gmail-meta3, gmail-meta3-LAB-9999, vb. İçin anlamına gelir.
  • [[ ! "$f" == *"$2" ]] &&: yukarıdakiyle $faynıdır f. $2olduğu .bakkabul parametresi. Bu, dosya adı bitmezse .bak(kopyalamak .bakve oluşturmak istemediğimizden .bak.bak) anlamına gelirse , aşağıdakileri yapın
  • cp -a "$f" "$f$2"; gmail-meta3'ü gmail-meta3.bak vb. kopyalayın.
  • done: geri dön ve gmail-meta3* listesindeki sonraki dosya adını al .

cps gmail-meta3 .bak Örnek Çıktı

Soruyu burada örnek olarak kullanmak, nasıl işlediğini görmektir:

───────────────────────────────────────────────────────────────────────────────────────────
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$ 

Not: Bu, zaman damgalarını korumak ve dosya yedeklemelerinizi daha iyi kavramak -aiçin cpkomutu içeren bayrağı kullanır .

Dosya kopyalarının asıllarla aynı tarih ve saate nasıl sahip olduğuna dikkat edin. Eğer -aparametre atlandı onlar geçerli tarih ve saati verilecek ve dosya boyutu aynı olacağını dışında gerçek bir yedekleme gibi bakmak olmaz.


6
insanlar her zaman ls
ayrışmaya

3
Bahsettiğinizden beri findayrışmanın tehlikelerinin farkında olduğunuzu varsayıyorum ls? Ancak sizin durumunuzda da gerekli değildir: sadece yapın for file in "$1"*; do copy -a "$file" "$file$2"; done- bu tamamen lsveya findher whiledöngüden veya bir döngüden daha basittir .
Konrad Rudolph

@KonradRudolph Öneriniz için teşekkürler. Önerinizi birkaç küçük değişiklikle uyguladım ve test ettim.
WinEunuuchs2Unix

2

Gereksiniminizi karşılamanın başka bir yolu, dosyaları geçici bir dizine kopyalamak ve renameyeniden adlandırmak için komutu kullanmaktır .

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

Bir komut dosyası olarak ihtiyacınız varsa, bunu böyle kullanabilirsiniz

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" .
}

Ve bunu şöyle kullanabilirsiniz:

cps file bak

Bu bir örnektir

$ 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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.