Bir klasörü tekrar tekrar cp kullanarak idempotent bir şekilde nasıl kopyalanır?


46

Kullandığımda

cp -R inputFolder outputFolder

sonuç bağlama bağımlıdır :

  1. yoksa outputFolder, yaratılır ve klonlanan klasör yolu olur outputFolder.
  2. eğer outputFoldervarsa, o zaman yaratılan klonoutputFolder/inputFolder

Bu korkunç , çünkü bazı kurulum betiği oluşturmak istiyorum ve eğer kullanıcı onu iki kez yanlışlıkla çalıştırırsa outputFolder, ilk kez yaratmış olacak , sonra ikinci çalıştırmada tüm şeyler bir kez daha yaratılacak outputFolder/inputFolder.

  • Her zaman ilk davranışı istiyorum: orijinalin yanında (bir kardeş olarak) bir klon oluşturun.
  • cpTaşınabilir olmak için kullanmak istiyorum (örneğin MINGW rsyncgönderilmedi)
  • Kontrol ettim cp -R --parentsama (klon olmayacak kadar tüm yol dizin ağacında yukarı bu yolu yeniden oluşturur outputFolderama some/path/outputFolder)
  • --remove-destinationveya --update2'nin hiçbir şeyi değiştirmemesi durumunda, yine de şeyler kopyalanır.outputFolder/inputFolder

Bunu outputFolder(eğer klasör yoksa o zaman ...) varlığını kontrol etmeden veya kullanmadan bunu yapmanın bir yolu var mı rm -rf outputFolder?

Bunu yapmanın kararlaştırılmış, taşınabilir UNIX yöntemi nedir?



2
Bu seçeneklerden bazıları POSIX değil, bu yüzden denedikleriniz çok taşınabilir değil. Biraz taşınabilirlik eksikliği ile yaşayabilirseniz, neden kullanmıyorsunuz rsync?
muru

1
Kullanmanız gerekmiyorsa cp, iki tarkomut arasında borulama yapmak ağaç kopyalamanın iyi ve güvenilir bir yoludur.
R. ..

@R .. bir örnek verebilir misiniz? @muru Bunun, MINGW'de rsyncgönderilmemiş olan çalışmasını istiyorum .
jakub.g 10:15

GNU katran ile tar -C input -cf - . | tar -C output -xf -çalışır. -Cçalışma dizinini değiştirir, ancak standart bir seçenek değildir, bu nedenle desteklenmiyorsa, başlamak için doğru çalışma dizinlerinde iki katranı çalıştırmanız gerekir, örneğin ( cd input && tar -cf - . ) | ( cd output && tar -xf - )Windows ile uğraşıyorsanız, sadece roaima'nın yanıtını kullanırdım.
R. ..

Yanıtlar:


54

Bunun yerine bunu kullanın:

cp -R inputFolder/. outputFolder

Bu, tam olarak aynı şekilde cp -R aaa/bbb cccçalışır , yani, çalışır: eğer cccyoksa, bir kopyası bbbve içeriği olarak yaratılır ; ama eğer ccczaten mevcutsa ccc/bbb, kopyası bbbve içeriği olarak yaratılır .

Bunun hemen hemen her örneği bbbiçin Sorunuza not ettiğiniz istenmeyen davranışları verir. Bununla birlikte, bu özel durumda bbb, sadece ., yani aaa/bbb, gerçekten de aaa/., sadece sırayla aaaancak başka bir adla. Bu yüzden bu iki senaryo var:

  1. ccc bulunmuyor:

    Komut cp -R aaa/. cccaracı "oluşturmak cccve içeriğini kopyalamak aaa/.içine ccc/., yani kopyalama aaaiçine ccc.

  2. ccc var mı:

    Komut cp -R aaa/. cccvasıtaları "içeriğini kopyalamak aaa/.içine ccc/., yani kopyalama aaaiçine ccc.


3
Huh, bu benim için yeni bir tanesi ama işe yarıyor! Bu herhangi bir yerde belgelendi mi?
Ilmari Karonen

1
Bunun inputFolder/.yerine test ettim inputFolderve gerçekten de Windows / Git Bash'de çalışıyor. (Ancak sadece bir izlemeyle çalışmıyor /, eğik çizgiden sonraki noktaya da ihtiyacım var) İlginç olduğundan +1 verdim, ancak genel kodda kullanmak biraz zor olsa da :)
jakub.g

@IlmariJaronen açıkça belgelenmesi gerekmez. Açıklamayı takip edin ve basitçe "kurallara uyduklarını" göreceksiniz.
roaima

18

Klasörü kopyalamayın, yalnızca içeriği kopyalayın:

## Create the target directory. The -p suppresses error messages
## if the directory already exists
mkdir -p outputFolder

## Copy the contents recursively, this will not recreate the parent
cp -R inputfolder/* outputfolder/

Bu şekilde, hem hedef dizinin komut dosyası ilk kez çalıştırıldığında oluşturulmasını sağlarsınız, hem de ikinci kez çalıştırırken sorunu önlersiniz.

Chris Down çok doğru bir şekilde bash'ta bunun adı a ile başlayan dosyaları atlayacağına işaret ediyor .. Bunu önlemek shopt -s dotglobiçin yukarıdaki komutu çalıştırmadan önce çalıştırabilirsiniz.

Hem -piçinmkdir ve -Riçincp bu mükemmel taşınabilir olmalı, böylece POSIX tarafından tanımlanmıştır.


6
Not: bu, ile başlayan dosyaları kopyalamaz .. Kısacası, dotglobbunun için kullanabilirsiniz .
Chris Down

2
Glob genişlemesi maksimum komut satırı uzunluğunu aşabileceğinden, giriş klasöründeki dizin girişlerinin sayısı büyükse bu yöntemin başarısız olması muhtemeldir.
Tim Cutts


4

Ayrıca rsyncşunları da kullanabilirsiniz :

rsync -uav inputFolder/ outputFolder/

(özellikle birinciden sonra, eğik çizgileri not edin)


0

Benim düşünceme göre, daha basit ve daha taşınabilir bir şey yoktur rm -rf outputFolder. Böylece hep buna sadık kalırım. Sorunuzun farklı olduğunu biliyorum, ancak bunun en iyi uygulama olduğunu düşünüyorum.


Hedefte tutulması gereken belirli izinler veya sahiplikler varsa, bu başarısız olur. Veya bir sembolik link ise.
roaima

1
Soru cp, hem dizin olmadığında hem de var olduğunda sonucun aynı olmasını istiyor . Peki ne hakkında konuşuyorsun?
dashohoxha

cpOP tarafından verildiği gibi komut izinleri (veya zaman damgalarını) tutmaz.
roaima

0

Komut -tseçeneğini şu şekilde kullanabilirsiniz cp:

cp -R inputFolder -t outputFolder

Şimdi eğer hedef klasör mevcut değilse bir hata atar:

cp: failed to access ‘outputFolder’: No such file or directory

Yukarıdaki not inputFolder, içeriğiyle birlikte kopyalanacaktır (yalnızca içindeki içerikleri değil)

arasında sadece içeriğini kopyalamak istiyorsanız inputFoldero biraz zor alır (yıldız işareti kullanırken globbing kabuk kullanırken dikkatli olmak zorunda çünkü *)

cp -R  -t outputFolder/ -- inputFolder/*

Şimdi eğer hedef klasör mevcut değilse bir hata atar:

cp: failed to access ‘outputFolder’: No such file or directory

ile çalışır cp (GNU coreutils) 8.23

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.