Bir dosyanın her satırı için bir komutu nasıl çalıştırırsınız?


161

Örneğin, şu anda Unix yollarını bir dosyaya yazdığım birkaç dosyayı değiştirmek için aşağıdakileri kullanıyorum:

cat file.txt | while read in; do chmod 755 "$in"; done

Daha zarif ve daha güvenli bir yol var mı?

Yanıtlar:


127

Bir dosyayı satır satır okuyun ve komutları yürütün: 4 yanıt

Çünkü sadece 1 cevap yok ...

  1. shell komut satırı genişletme
  2. xargs özel araç
  3. while read bazı sözlerle
  4. while read -uetkileşimli işleme fdiçin özel kullanarak (örnek)

OP isteği ilgili olarak: çalışan chmoddosyasında yer alan tüm hedeflere , xargsbelirtilen araçtır. Ancak diğer bazı uygulamalar için az miktarda dosya vb.

  1. Dosyanın tamamını komut satırı bağımsız değişkeni olarak okuyun.

    Dosyanız çok büyük değilse ve tüm dosyalar iyi adlandırılmışsa (boşluklar veya tırnak işaretleri gibi diğer özel karakterler olmadan), shellkomut satırı genişletmesini kullanabilirsiniz . basitçe:

    chmod 755 $(<file.txt)

    Az miktarda dosya (satır) için bu komut daha açıktır.

  2. xargs doğru araç

    Daha fazla sayıda dosya veya giriş dosyanızdaki neredeyse her sayıda satır için ...

    Birçokları için binutils'ler araçları gibi chown, chmod, rm, cp -t...

    xargs chmod 755 <file.txt

    Özel karakterleriniz ve / veya çok fazla satırınız varsa file.txt.

    xargs -0 chmod 755 < <(tr \\n \\0 <file.txt)

    komutunuzun girişle tam olarak 1 kez çalıştırılması gerekiyorsa:

    xargs -0 -n 1 chmod 755 < <(tr \\n \\0 <file.txt)

    Bu örnek için bu gerekli değildir, çünkü chmodçoklu dosyaları bağımsız değişken olarak kabul et, ancak bu soru başlığıyla eşleşir.

    Bazı özel durumlar için, oluşturulan komutlarda dosya bağımsız değişkeninin konumunu şu şekilde tanımlayabilirsiniz xargs:

    xargs -0 -I '{}' -n 1 myWrapper -arg1 -file='{}' wrapCmd < <(tr \\n \\0 <file.txt)

    seq 1 5Giriş olarak test et

    Bunu dene:

    xargs -n 1 -I{} echo Blah {} blabla {}.. < <(seq 1 5)
    Blah 1 blabla 1..
    Blah 2 blabla 2..
    Blah 3 blabla 3..
    Blah 4 blabla 4..
    Blah 5 blabla 5..

    Komutanın her satıra bir kez yapıldığı yer .

  3. while read ve varyantlar.

    OP'nin önereceği gibi cat file.txt | while read in; do chmod 755 "$in"; done, ancak 2 sorun var:

    • cat |Bir olan gereksiz çatal ve

    • | while ... ;doneçevrenin sonradan kaybolacağı bir alt kabuk olacak ;done.

    Yani bu daha iyi yazılabilir:

    while read in; do chmod 755 "$in"; done < file.txt

    Fakat,

    • Hakkında uyarılabilir $IFSve readişaretler alabilirsiniz:

      help read
      read: read [-r] ... [-d delim] ... [name ...]
          ...
          Reads a single line from the standard input... The line is split
          into fields as with word splitting, and the first word is assigned
          to the first NAME, the second word to the second NAME, and so on...
          Only the characters found in $IFS are recognized as word delimiters.
          ...
          Options:
            ...
            -d delim   continue until the first character of DELIM is read, 
                       rather than newline
            ...
            -r do not allow backslashes to escape any characters
          ...
          Exit Status:
          The return code is zero, unless end-of-file is encountered...

      Bazı durumlarda,

      while IFS= read -r in;do chmod 755 "$in";done <file.txt

      Yabancı adlarla ilgili sorunlardan kaçınmak için. Ve belki de aşağıdakilerle ilgili problemleri çözerseniz UTF-8:

      while LANG=C IFS= read -r in ; do chmod 755 "$in";done <file.txt
    • STDINOkuma için kullandığınız sırada file.txt, komut dosyanız etkileşimli olamazdı ( STDINartık kullanamazsınız ).

  4. while read -u, özel kullanarak fd.

    Söz dizimi: while read ...;done <file.txtyönlendirir STDINiçin file.txt. Bu, bitene kadar süreçle başa çıkamayacağınız anlamına gelir.

    Etkileşimli bir araç oluşturmayı planlıyorsanız , STDINbazı alternatif dosya tanımlayıcıları kullanmaktan kaçınmanız ve kullanmanız gerekir .

    Sabitler dosya tanımlayıcıları şunlardır: 0için STDIN , 1için STDOUT ve 2için STDERR . Bunları şu şekilde görebilirsiniz:

    ls -l /dev/fd/

    veya

    ls -l /proc/self/fd/

    Oradan, dosya tanımlayıcı olarak 0ve arasında 63(ve aslında sysctlsüper kullanıcı aracına bağlı olarak ) arasında kullanılmayan numarayı seçmelisiniz :

    Bu demo için fd kullanacağım 7:

    exec 7<file.txt      # Without spaces between `7` and `<`!
    ls -l /dev/fd/

    Sonra şu şekilde kullanabilirsiniz read -u 7:

    while read -u 7 filename;do
        ans=;while [ -z "$ans" ];do
            read -p "Process file '$filename' (y/n)? " -sn1 foo
            [ "$foo" ]&& [ -z "${foo/[yn]}" ]&& ans=$foo || echo '??'
        done
        if [ "$ans" = "y" ] ;then
            echo Yes
            echo "Processing '$filename'."
        else
            echo No
        fi
    done 7<file.txt

    done

    Kapatmak için fd/7:

    exec 7<&-            # This will close file descriptor 7.
    ls -l /dev/fd/

    Not: Paralel işlemle birçok I / O yaparken bu sözdiziminin yararlı olabileceği için çarpıcı sürüme izin verdim:

    mkfifo sshfifo
    exec 7> >(ssh -t user@host sh >sshfifo)
    exec 6<sshfifo

3
As xargsgibi, muhtaç, bazı özellikler bu tür cevaplanması için initialy inşa edildi cari ortamında uzun mümkün olduğunca komutu bina talebinde bulunmak için chmod, mümkün olduğunca az bu durumda azaltılması çatallar efficience sağlamak. while ;do..done <$file1 dosya için 1 çatal çalışan implie. xargsbin dosya için 1 çatal çalıştırabilir ... güvenilir bir şekilde.
F. Hauri

1
üçüncü komut neden bir makefile içinde çalışmıyor? i "sözdizimi hatası beklenmedik belirteci <<" yakın alıyorum, ancak doğrudan komut satırından yürütme çalışır.
Woodrow Barlow

2
Bu Makefile'a özel sözdizimiyle bağlantılı görünmektedir. Komut satırını tersine çevirmeyi deneyebilirsiniz:cat file.txt | tr \\n \\0 | xargs -0 -n1 chmod 755
F. Hauri

@ F.Hauri herhangi bir nedenle, tr \\n \\0 <file.txt |xargs -0 [command]tarif ettiğiniz yöntemden yaklaşık% 50 daha hızlıdır.
phil294

Ekim 2019, yeni düzenleme, etkileşimli dosya işlemci örneği ekleyin .
F. Hauri

150

Evet.

while read in; do chmod 755 "$in"; done < file.txt

Bu şekilde bir catişlemden kaçınabilirsiniz .

catböyle bir amaç için neredeyse her zaman kötüdür. Yararsız Kedi Kullanımı hakkında daha fazla bilgi edinebilirsiniz .


Kaçının biri cat iyi bir fikirdir, ancak bu durumda, belirtilen komutxargs
F. Hauri

Bu bağlantı alakalı görünmüyor, belki de web sayfasının içeriği değişti? Cevabın geri kalanı yine de harika :)
starbeamrainbowlabs

@starbeamrainbowlabs Evet. Sayfa taşınmış gibi görünüyor. Yeniden bağlandım ve şimdi iyi olmalı. Teşekkürler :)
PP

1
Teşekkürler! Bu, özellikle çağırmaktan başka bir şey yapmanız gerektiğinde yardımcı oldu chmod(yani dosyadaki her satır için gerçekten bir komut çalıştırın).
Per Lundberg

ters eğik çizgilere dikkat edin! dan unix.stackexchange.com/a/7561/28160 - " read -r(standart girişten tek bir satır okur readolmadan -rbunu istemiyoruz, yorumlayan ters eğik)."
Brezilyalı Adam

16

güzel bir seçiciniz varsa (örneğin bir dizindeki tüm .txt dosyaları) şunları yapabilirsiniz:

for i in *.txt; do chmod 755 "$i"; done

döngü için bash

veya bir varyantınız:

while read line; do chmod 755 "$line"; done <file.txt

Çalışmayan şey, satırda boşluklar varsa, girdinin satırlara göre boşluklara bölünmesidir.
Michael Fox

@Michael Fox: Boşluklu çizgiler ayırıcı değiştirilerek desteklenebilir. Yeni satırlara değiştirmek için, komut dosyasından / komuttan önce 'IFS' ortam değişkenini ayarlayın. Örn: dışa aktarma IFS = '$ \ n'
codesniffer

Son yorumda yazım hatası. Olması gereken: export IFS = $ '\ n'
codesniffer

14

Girişte boşluk olmadığınızı biliyorsanız:

xargs chmod 755 < file.txt

Yollarda boşluk varsa ve GNU xargs'ınız varsa:

tr '\n' '\0' < file.txt | xargs -0 chmod 755

Xargs'ı biliyorum, ama (ne yazık ki) while ve read gibi yerleşik özelliklerden daha az güvenilir bir çözüm gibi görünüyor. Ayrıca, GNU xargs yok, ama OS X kullanıyorum ve xargs da burada -0 seçeneği var. Cevap için teşekkürler.
şahin

1
@hawk No: xargssağlamdır. Bu araç çok eskidir ve kodu şiddetle tekrar gözden geçirilir . Amacı başlangıçta mermi sınırlamaları (64kchar / line falan) konusunda çizgiler oluşturmaktı. Şimdi bu araç çok büyük dosyalarla çalışabilir ve son komuta çatal sayısını çok azaltabilir. Cevabımı gör ve / veya man xargs.
F. Hauri

@hawk Hangi yoldan daha az güvenilir çözüm? Linux, Mac / BSD ve Windows'ta çalışıyorsa (evet, MSYSGIT'in paketleri GNU xargs), o kadar güvenilirdir.
Camilo Martin

1
Bunu arama sonuçlarından bulmaya devam edenler için ... Homebrew kullanarak macOS'a GNU xargs yükleyebilirsiniz (brew install findutils ) GNU xargs kurabilir ve gxargsbunun yerine GNU xargs'ı çağırabilirsiniz , örneğingxargs chmod 755 < file.txt
Jase

13

Komutunuzu her satır için paralel olarak çalıştırmak istiyorsanız GNU Paralel kullanabilirsiniz

parallel -a <your file> <program>

Dosyanızın her satırı bağımsız değişken olarak programa aktarılır. Varsayılan parallelolarak CPU'larınızın saydığı sayıda iş parçacığı çalıştırır. Ama ile belirtebilirsiniz-j


3

Bash etiketlediğinizi görüyorum, ancak Perl de bunu yapmanın iyi bir yolu olurdu:

perl -p -e '`chmod 755 $_`' file.txt

Doğru dosyaları aldığınızdan emin olmak için bir regex uygulayabilirsiniz, örneğin yalnızca .txt dosyalarını işlemek için:

perl -p -e 'if(/\.txt$/) `chmod 755 $_`' file.txt

Olanları "önizlemek" için, ters tırnakları çift tırnak işareti ile değiştirin ve başına print:

perl -p -e 'if(/\.txt$/) print "chmod 755 $_"' file.txt

2
Neden ters iğne kullanılır? Perl bir chmodişlevi var
Glenlen Jackman

1
Sen istersin perl -lpe 'chmod 0755, $_' file.txt- kullanım -l"otomatik chomp" özelliği için
glenn Jackman



0

Geç olduğunu biliyorum ama yine de

Herhangi bir şans eseri \r\nyerine, windows kaydedilmiş metin dosyası ile \nçalışırsanız, komutunuz argüman olarak okuma satırından sonra sth varsa, çıktı ile karışabilirsiniz. Öyleyse kaldırın \r, örneğin:

cat file | tr -d '\r' | xargs -L 1 -i echo do_sth_with_{}_as_line
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.