Làm thế nào để thực thi một lệnh bash trong tất cả các thư mục con có thể?


7

Hãy nói rằng thư mục chính của tôi là /home/test

Theo nó tôi có rất nhiều thư mục con và dưới thư mục con vẫn còn rất nhiều trong số họ và như vậy.

Thí dụ:

/home/test
/home/test/subdir1
/home/test/subdir1/subdir2
/home/test/subdir1/subdir2/subdir3
/home/test/subdir1/subdir2/subdir4
/home/test/subdir1/subdir2/subdir4/subdir5

và như thế ...

Tôi muốn một kịch bản đơn giản có mỗi thư mục và chỉ cần chạy pwdlệnh.


3
Điểm của bài tập này là gì? Có phải nó chỉ đơn giản là để tạo ra một danh sách các tên của tất cả các thư mục? Hoặc, như tiêu đề của bạn cho thấy, bạn có một số lệnh khác mà bạn muốn gọi trong mỗi thư mục và bạn chỉ sử dụng pwdnhư một bằng chứng về khái niệm?
G-Man nói 'Phục hồi Monica'

youweek1, bên dưới, bạn nhận xét "pwd chỉ là một ví dụ ... tôi cần chạy nhiều lệnh hơn trong mỗi thư mục con" - lưu ý pwdlà khá đặc biệt theo nhiều cách - đó là nguyên nhân gây nhầm lẫn vì nó được sử dụng làm lệnh ví dụ.
Volker Siegel

Câu trả lời:


7

Giải pháp sử dụng song song

Bạn có thể sử dụng GNU Parallel cho một giải pháp nhỏ gọn, nhanh hơn.

find . -type d -print0 | parallel -0 cd {}'&&' <command-name>

Điều này sẽ hoạt động hoàn toàn tốt, ngay cả đối với tên thư mục chứa khoảng trắng và dòng mới. Điều gì parallelở đây là nó lấy đầu ra từ find, đó là mọi thư mục và sau đó đưa nó vào cd bằng cách sử dụng {}. Sau đó, nếu thay đổi thư mục thành công, một lệnh riêng biệt được chạy trong thư mục này.

Giải pháp thường xuyên sử dụng vòng lặp while

find "$PWD" -type d | while read -r line; do cd "$line" && <command-name>; done;

Lưu ý rằng $PWDđược sử dụng ở đây vì biến đó chứa đường dẫn tuyệt đối của thư mục hiện tại từ nơi lệnh đang được chạy. Nếu bạn không sử dụng đường dẫn tuyệt đối, thì cdcó thể ném lỗi trong vòng lặp while.

Đây là một giải pháp dễ dàng hơn. Nó sẽ hoạt động hầu hết thời gian trừ khi tên thư mục có các ký tự lạ trong đó như dòng mới (xem bình luận).


Không chắc chắn làm thế nào mạnh mẽ này sẽ làm việc khác, nhưng ít nhất các mở rộng biến cần phải được trích dẫn. Hãy thử với một khoảng trắng trong $PWDhoặc một số tên thư mục con.
Volker Siegel

1
Những thất bại cho tên thư mục có chứa dòng mới.
G-Man nói 'Phục hồi Monica'

1
Cái thứ hai cũng không thành công cho tên thư mục kết thúc bằng khoảng trống. Ngoài ra còn có một vấn đề là lệnh được chạy bất kể cdthành công hay không.
Stéphane Chazelas 17/05/2015

1
@shivams: Bạn thường muốn sử dụng -print0với find, để có được đầu ra ở dạng không rõ ràng (tách biệt nul thay vì phân tách dòng mới). Đối với ví dụ thứ hai, sử dụng read -d $'\0' -r lineđể đọc đầu ra từ find. Đối với cái đầu tiên, hãy kiểm tra xem parallelcó một tùy chọn nào cho biết nó mong đợi đầu vào được phân tách không thay vì phân tách dòng mới; nó có thể được gọi là một cái gì đó giống như -0, -zhoặc --null. (Hoặc, đối với một cách tiếp cận khác, hãy xem câu trả lời của tôi.)
G-Man nói 'Tái lập lại Monica'

2
GNU Parallel có -0 và --null. OP viết rằng ông có thư mục con. Vì vậy, nó có khả năng không phải là một người dùng độc hại. Tôi vẫn chưa thấy tên thư mục với \ n được tạo bởi người dùng không độc hại. Không gian, vâng. Báo giá, vâng. Ngôi sao, dấu ngã và đường ống, vâng. Nhưng không bao giờ \ n.
Ole Tange

5

Các findlệnh là mạnh mẽ, nhưng mà làm cho nó một chút thách thức để sử dụng.
Tôi khá chắc chắn rằng nó có thể làm những gì bạn cần - lệnh này bên dưới là "gần như" những gì bạn yêu cầu:

find . -type d -execdir pwd \;

Nhưng - điều này không chạy lệnh ở cấp thư mục sâu nhất - nó chạy trong các thư mục chứa các thư mục khác.

Vì vậy, nó sẽ chạy vào subdir4, bởi vì nó chứa subdir5. Nhưng không phải trong subdir5- như bạn có thể mong đợi.

Điều quan trọng là -execdirtùy chọn được sử dụng, không phải là phổ biến hơn -exec(xem man find):

Các -exectùy chọn chạy lệnh trong thư mục khởi động, và có nhược điểm khác nữa:

    -exec command ;
           Execute command; true if 0 status  is  returned.   All  following
           arguments  to find are taken to be arguments to the command until
           an argument consisting of ';' is encountered.  The string '{}' is
           replaced  by  the current file name being processed everywhere it
           occurs in the arguments to the command,  not  just  in  arguments
           where  it  is  alone, as in some versions of find.  Both of these
           constructions might need to be escaped (with a \ ) or quoted  to
           protect  them from expansion by the shell.  See the EXAMPLES sec
           tion for examples of the use of the -exec option.  The  specified
           command  is  run once for each matched file.  The command is exe
           cuted in the starting directory.   There are unavoidable security
           problems  surrounding use of the -exec action; you should use the
          -execdir option instead.

Nhưng tùy chọn -execdirchỉ là những gì bạn yêu cầu:

   -execdir command ;
   -execdir command {} +
           Like  -exec,  but the specified command is run from the subdirec
           tory containing the matched  file,  which  is  not  normally  the
           directory  in  which  you  started find.  This a much more secure
           method for invoking commands, as it avoids race conditions during
           resolution  of the paths to the matched files.  As with the -exec
           action, the '+' form of -execdir will build  a  command  line  to
           process  more  than one matched file, but any given invocation of
           command will only list files that exist in the same subdirectory.
           If  you use this option, you must ensure that your $PATH environ
           ment variable does not reference '.'; otherwise, an attacker  can
           run any commands they like by leaving an appropriately-named file
           in a directory in which you will run -execdir.  The same  applies
           to having entries in $PATH which are empty or which are not abso
           lute directory names.

1
Hmm ... điều này sẽ chạy cùng một lệnh nhiều lần trên cùng một thư mục có nhiều con (ví dụ: nếu có 100 thư mục một cấp dưới dir1nó sẽ execdir pwd100 lần).
don_crissti 17/05/2015

Ồ, đó là sự thật ...
Volker Siegel

1

Một trong những câu trả lời khác gần với điều này:

tìm thấy . -type d -exec sh -c 'cd "$ 0" && cmd ' {} \;

(chỉ chạy lệnh nếu cdthành công). Một số người khuyên nên chèn một đối số giả, vì vậy thư mục tìm thấy ( {}) trượt qua $1:

tìm thấy . -type d -exec sh -c 'cd "$ 1" && cmd ' foo {} ";"

Tất nhiên, bạn có thể sử dụng bất kỳ chuỗi nào ở đây thay cho foo. Lựa chọn phổ biến bao gồm -, --sh. Chuỗi này sẽ được sử dụng trong các thông báo lỗi (nếu có), ví dụ:

foo: dòng 0: cd : rict_directory : Quyền bị từ chối

vì vậy shdường như là một lựa chọn tốt  \;";"hoàn toàn tương đương; đó chỉ là một sở thích phong cách.

Các lệnh trên thực thi shell một lần cho mỗi thư mục. Điều này có thể đi kèm với một số hình phạt hiệu suất (đặc biệt nếu lệnh được thực thi là tương đối nhẹ). Các lựa chọn thay thế bao gồm

tìm thấy . -type d -exec sh -c 'cho d; làm (cd "$ d" && cmd ); xong 'sh {} +

trong đó thực thi shell một lần, nhưng chuyển đổi một lần cho mỗi thư mục và

tìm thấy . -type d -exec sh -c 'save_d = $ PWD; cho d; làm cd "$ d" && cmd ; cd "$ save_d"; xong 'sh {} +

mà không sinh ra bất kỳ subshells. Nếu bạn đang tìm kiếm một đường dẫn tuyệt đối, như câu hỏi của bạn cho thấy, bạn có thể kết hợp những điều trên và làm

tìm / nhà / kiểm tra -type d -exec sh -c 'cho d; làm cd "$ d" && cmd ; xong 'sh {} +

không sinh ra bất kỳ mạng con nào và cũng không phải bận tâm đến việc lưu điểm bắt đầu.


Lưu ý rằng các vỏ POSIX đã được đặt $OLDPWD, vì vậy bạn không cần phải lưu thêm một bản sao $save_d. Cú pháp POSIX để lặp qua "$ @" là for d dokhông for d; do(mặc dù sau này cũng được hỗ trợ rộng rãi (ngoại trừ trong vỏ Bourne không phải là vỏ POSIX).
Stéphane Chazelas 17/05/2015

Có, nhưng nếu cmdđược thay thế bằng một chuỗi các lệnh bao gồm các cdlệnh khác , thì nó $OLDPATHsẽ không còn là thư mục mà nó findđang chạy.
G-Man nói 'Phục hồi Monica'

Thật. Lưu ý rằng ngoại trừ việc triển khai sh bashpdkshdựa trên sh, nếu cmdlà lệnh bên ngoài, (cd "$d" && cmd)sẽ không rẽ nhánh một quá trình bổ sung (so với cùng không có lớp con) vì cmdđược thực thi trực tiếp trong quy trình subshell, vì vậy sẽ rất xấu hổ khi tránh nó căn cứ thực hiện trong những trường hợp.
Stéphane Chazelas 17/05/2015

Hữu ích để biết. Nhưng, một lần nữa, tôi tin rằng bạn cho rằng đó cmdlà một lệnh duy nhất .  (cd "$d" && cmd₁ && cmd₂)sẽ cần phải rẽ nhánh một quá trình, phải không?
G-Man nói 'Phục hồi Monica'

Không phải nếu cmd cuối cùng là bên ngoài (và vỏ không phải là bash hoặc pdksh dựa trên)
Stéphane Chazelas

0

Bạn không cần chạy thêm pwd lệnh, bạn có thể chạy lệnh sau:

find test/ -type d

Bạn có thể thay thế thư mục của bạn với test/


pwd chỉ là một ví dụ ... tôi cần chạy nhiều lệnh hơn trong mỗi thư mục con
youweek1 17/05/2015

@ youweek1 pwdkhá đặc biệt theo nhiều cách - đó là nguyên nhân gây nhầm lẫn.
Volker Siegel

0

Bạn có thể sử dụng một cái gì đó như thế này

find /home/test/ -type d -exec bash -c '<Command to be executed>' \; 

Thí dụ:

Lệnh dưới đây sẽ hiển thị dirname, tương tự như chạy lệnh pwd.

find /home/test/ -type d -exec bash -c 'echo $0' \;

Lỗi find: missing argument to ném : -exec'`
shivams 17/05/2015
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.