Làm thế nào để bạn chạy một lệnh cho mỗi dòng của một tập tin?


161

Ví dụ: ngay bây giờ tôi đang sử dụng cách sau để thay đổi một vài tệp có đường dẫn Unix tôi đã viết thành tệp:

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

Có cách nào thanh lịch hơn, an toàn hơn?

Câu trả lời:


127

Đọc một dòng tệp theo dòng và thực hiện các lệnh: 4 câu trả lời

Điều này là do không chỉ có 1 câu trả lời ...

  1. shell mở rộng dòng lệnh
  2. xargs công cụ chuyên dụng
  3. while read với một số nhận xét
  4. while read -usử dụng chuyên dụng fd, để xử lý tương tác (mẫu)

Về yêu cầu OP: chạy chmodtrên tất cả các mục tiêu được liệt kê trong tệp , xargslà công cụ được chỉ định. Nhưng đối với một số ứng dụng khác, số lượng tệp nhỏ, v.v ...

  1. Đọc toàn bộ tệp dưới dạng đối số dòng lệnh.

    Nếu tệp của bạn không quá lớn và tất cả các tệp đều được đặt tên tốt (không có dấu cách hoặc ký tự đặc biệt khác như dấu ngoặc kép), bạn có thể sử dụng shellmở rộng dòng lệnh . Đơn giản:

    chmod 755 $(<file.txt)

    Đối với số lượng nhỏ tệp (dòng), lệnh này là nhẹ hơn.

  2. xargs là công cụ phù hợp

    Đối với số lượng tệp lớn hơn hoặc gần như bất kỳ số lượng dòng nào trong tệp đầu vào của bạn ...

    Đối với nhiều binutils công cụ, như chown, chmod, rm, cp -t...

    xargs chmod 755 <file.txt

    Nếu bạn có ký tự đặc biệt và / hoặc rất nhiều dòng trong file.txt.

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

    nếu lệnh của bạn cần được chạy chính xác 1 lần bằng cách nhập:

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

    Điều này là không cần thiết cho mẫu này, vì chmodchấp nhận nhiều tệp làm đối số, nhưng điều này phù hợp với tiêu đề của câu hỏi.

    Đối với một số trường hợp đặc biệt, bạn thậm chí có thể xác định vị trí của đối số tệp trong các lệnh được tạo bởi xargs:

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

    Kiểm tra với seq 1 5đầu vào

    Thử cái này:

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

    Trường hợp Commande được thực hiện một lần trên mỗi dòng .

  3. while read và các biến thể.

    Theo đề xuất của OP cat file.txt | while read in; do chmod 755 "$in"; donesẽ hoạt động, nhưng có 2 vấn đề:

    • cat |là một ngã ba vô dụng , và

    • | while ... ;donesẽ trở thành một subshell nơi môi trường sẽ biến mất sau đó ;done.

    Vì vậy, điều này có thể được viết tốt hơn:

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

    Nhưng,

    • Bạn có thể được cảnh báo $IFSreadcờ:

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

      Trong một số trường hợp, bạn có thể cần phải sử dụng

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

      Để tránh các vấn đề với tên tập tin lạ. Và có thể nếu bạn gặp sự cố với UTF-8:

      while LANG=C IFS= read -r in ; do chmod 755 "$in";done <file.txt
    • Trong khi bạn sử dụng STDINđể đọc file.txt, tập lệnh của bạn không thể tương tác (bạn không thể sử dụng STDINnữa).

  4. while read -u, sử dụng chuyên dụng fd.

    Cú pháp: while read ...;done <file.txtsẽ chuyển hướng STDINđến file.txt. Điều đó có nghĩa là, bạn sẽ không thể xử lý quá trình cho đến khi họ hoàn thành.

    Nếu bạn có kế hoạch tạo công cụ tương tác , bạn phải tránh sử dụng STDINvà sử dụng một số mô tả tệp thay thế .

    Các mô tả tệp hằng là: 0cho STDIN , 1cho STDOUT2cho STDERR . Bạn có thể thấy chúng bằng cách:

    ls -l /dev/fd/

    hoặc là

    ls -l /proc/self/fd/

    Từ đó, bạn phải chọn số chưa sử dụng, giữa 063(trên thực tế, tùy thuộc vào sysctlcông cụ siêu người dùng) làm mô tả tệp :

    Đối với bản demo này, tôi sẽ sử dụng fd 7 :

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

    Sau đó, bạn có thể sử dụng read -u 7cách này:

    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

    Để đóng fd/7:

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

    Nota: Tôi để striked phiên bản vì cú pháp này có thể là hữu ích, khi làm nhiều I / O với sự tương đồng xử lý:

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

3
Như xargsđã được xây dựng ban đầu để đáp ứng nhu cầu này, một số tính năng, như lệnh xây dựng càng lâu càng tốt trong môi trường hiện tại để gọi chmodtrong trường hợp này càng ít càng tốt, giảm dĩa đảm bảo hiệu quả. while ;do..done <$fileimplie chạy 1 fork cho 1 file. xargscó thể chạy 1 ngã ba cho hàng ngàn tệp ... một cách đáng tin cậy.
F. Hauri

1
Tại sao lệnh thứ ba không hoạt động trong tệp thực hiện? Tôi đang nhận được "lỗi cú pháp gần mã thông báo bất ngờ` <'", nhưng thực thi trực tiếp từ dòng lệnh hoạt động.
Woodrow Barlow

2
Điều này dường như được liên kết với cú pháp cụ thể Makefile. Bạn có thể thử đảo ngược dòng lệnh:cat file.txt | tr \\n \\0 | xargs -0 -n1 chmod 755
F. Hauri

@ F.Hauri vì một số lý do, tr \\n \\0 <file.txt |xargs -0 [command]nhanh hơn khoảng 50% so với phương pháp bạn mô tả.
phil294

Tháng 10 năm 2019, chỉnh sửa mới, thêm mẫu xử lý tệp tương tác .
F. Hauri

150

Đúng.

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

Bằng cách này bạn có thể tránh được một catquá trình.

cathầu như luôn luôn xấu cho một mục đích như thế này. Bạn có thể đọc thêm về Sử dụng Mèo vô dụng.


Tránh một cat là một ý tưởng tốt, nhưng trong trường hợp này, các lệnh chỉ định làxargs
F. Hauri

Liên kết đó dường như không liên quan, có lẽ nội dung của trang web đã thay đổi? Phần còn lại của câu trả lời là tuyệt vời :)
starbeamrainbowlabs

@starbeamrainbowlabs Có. Có vẻ như trang đã được di chuyển. Tôi đã liên kết lại và sẽ ổn ngay bây giờ. Cảm ơn :)
PP

1
Cảm ơn! Điều này rất hữu ích, đặc biệt là khi bạn cần làm gì đó ngoài việc gọi chmod(tức là thực sự chạy một lệnh cho mỗi dòng trong tệp).
Per Lundberg

hãy cẩn thận với dấu gạch chéo ngược! từ unix.stackexchange.com/a/7561/28160 - " read -rđọc một dòng duy nhất từ ​​đầu vào tiêu chuẩn ( readkhông có -rdấu gạch chéo ngược, bạn không muốn điều đó)."
chàng người Brazil đó

16

nếu bạn có một bộ chọn đẹp (ví dụ: tất cả các tệp .txt trong một thư mục) bạn có thể làm:

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

bash cho vòng lặp

hoặc một biến thể của bạn:

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

Điều không hiệu quả là nếu có khoảng trắng trong dòng, đầu vào được phân chia theo khoảng trắng, không phải theo dòng.
Michael Fox

@Michael Fox: Các dòng có khoảng trắng có thể được hỗ trợ bằng cách thay đổi dấu phân cách. Để thay đổi nó thành dòng mới, hãy đặt biến môi trường 'IFS' trước tập lệnh / lệnh. Ví dụ: export IFS = '$ \ n'
mã số

Typo trong bình luận cuối cùng của tôi. Nên là: xuất IFS = $ '\ n'
mã số

14

Nếu bạn biết bạn không có bất kỳ khoảng trắng nào trong đầu vào:

xargs chmod 755 < file.txt

Nếu có thể có khoảng trắng trong các đường dẫn và nếu bạn có GNU xargs:

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

Tôi biết về xargs, nhưng (đáng buồn thay) có vẻ như là một giải pháp đáng tin cậy hơn so với các tính năng tích hợp sẵn trong khi đọc và đọc. Ngoài ra, tôi không có GNU xargs, nhưng tôi đang sử dụng OS X và xargs cũng có tùy chọn -0 ở đây. Cảm ơn câu trả lời.
diều hâu

1
@hawk Không: xargslà mạnh mẽ. Công cụ này rất cũ và mã của anh ấy được xem xét lại mạnh mẽ . Mục tiêu của anh là khởi đầu để xây dựng các dòng liên quan đến các giới hạn về vỏ (64kchar / dòng hoặc một cái gì đó như vậy). Bây giờ công cụ này có thể làm việc với các tệp rất lớn và có thể giảm rất nhiều số ngã ba đến lệnh cuối cùng. Xem câu trả lời của tôi và / hoặc man xargs.
F. Hauri

@hawk Ít giải pháp đáng tin cậy theo cách nào? Nếu nó hoạt động trong Linux, Mac / BSD và Windows (vâng, gói GNU xargs của MSYSGIT), thì nó đáng tin cậy như nó có.
Camilo Martin

1
Đối với những người đến vẫn đang tìm kiếm này từ kết quả tìm kiếm ... bạn có thể cài đặt xargs GNU trên hệ điều hành MacOS bằng cách sử dụng Homebrew ( brew install findutils), và sau đó invoke xargs GNU với gxargsthay vào đó, ví dụgxargs chmod 755 < file.txt
Jase

13

Nếu bạn muốn chạy lệnh song song cho mỗi dòng, bạn có thể sử dụng GNU Parallel

parallel -a <your file> <program>

Mỗi dòng tệp của bạn sẽ được chuyển đến chương trình dưới dạng đối số. Theo mặc định, parallelchạy nhiều chủ đề như số lượng CPU của bạn. Nhưng bạn có thể chỉ định nó với-j


3

Tôi thấy rằng bạn đã gắn thẻ bash, nhưng Perl cũng sẽ là một cách tốt để làm điều này:

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

Bạn cũng có thể áp dụng biểu thức chính quy để đảm bảo bạn nhận được đúng tệp, ví dụ: chỉ xử lý tệp .txt:

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

Để "xem trước" những gì đang xảy ra, chỉ cần thay thế backticks bằng dấu ngoặc kép và trả trước print:

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

2
Tại sao nên sử dụng backticks? Perl có một chmodchức năng
glenn jackman

1
Bạn muốn perl -lpe 'chmod 0755, $_' file.txt- sử dụng -lcho tính năng "tự động nhai"
glenn jackman

1

Bạn cũng có thể sử dụng AWK để có thể linh hoạt hơn để xử lý tệp

awk '{ print "chmod 755 "$0"" | "/bin/sh"}' file.txt

nếu tệp của bạn có dấu phân cách trường như:

trường1, trường2, trường3

Để chỉ nhận được lĩnh vực đầu tiên bạn làm

awk -F, '{ print "chmod 755 "$1"" | "/bin/sh"}' file.txt

Bạn có thể kiểm tra thêm chi tiết về Tài liệu GNU https://www.gnu.org/software/gawk/manual/html_node/Very-Simple.html#Very-Simple



0

Tôi biết muộn nhưng vẫn

Nếu bất kỳ khi nào bạn chạy vào windows đã lưu tệp văn bản \r\nthay vì \n, bạn có thể bị nhầm lẫn bởi đầu ra nếu lệnh của bạn có sth sau khi đọc dòng dưới dạng đối số. Vì vậy, loại bỏ \r, ví dụ:

cat file | tr -d '\r' | xargs -L 1 -i echo do_sth_with_{}_as_line
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.