Có ai có một kịch bản shell shell để làm một cái gì đó với lsmột danh sách các tên thư mục và lặp qua từng cái và làm một cái gì đó không?
Tôi dự định làm ls -1d */để có được danh sách tên thư mục.
Có ai có một kịch bản shell shell để làm một cái gì đó với lsmột danh sách các tên thư mục và lặp qua từng cái và làm một cái gì đó không?
Tôi dự định làm ls -1d */để có được danh sách tên thư mục.
Câu trả lời:
Đã chỉnh sửa không sử dụng ls nơi mà một quả địa cầu sẽ làm, như @ shawn-j-goff và những người khác đề xuất.
Chỉ cần sử dụng một for..do..donevòng lặp:
for f in *; do
echo "File -> $f"
done
Bạn có thể thay thế *bằng *.txthoặc bất kỳ quả cầu nào khác trả về danh sách (tệp, thư mục hoặc bất cứ thứ gì cho vấn đề đó), một lệnh tạo danh sách, ví dụ $(cat filelist.txt), hoặc thực sự thay thế nó bằng danh sách.
Trong dovòng lặp, bạn chỉ cần tham khảo biến vòng lặp với tiền tố ký hiệu đô la ( $ftrong ví dụ trên). Bạn có thể echonó hoặc làm bất cứ điều gì khác với nó bạn muốn.
Ví dụ: để đổi tên tất cả các .xmltệp trong thư mục hiện tại thành .txt:
for x in *.xml; do
t=$(echo $x | sed 's/\.xml$/.txt/');
mv $x $t && echo "moved $x -> $t"
done
Hoặc thậm chí tốt hơn, nếu bạn đang sử dụng Bash, bạn có thể sử dụng các mở rộng tham số Bash thay vì sinh ra một lớp con:
for x in *.xml; do
t=${x%.xml}.txt
mv $x $t && echo "moved $x -> $t"
done
forcác vòng lặp và một cạm bẫy điển hình của việc cố gắng phân tích đầu ra của ls. @ DanielA.White, bạn có thể xem xét không chấp nhận câu trả lời này nếu nó không hữu ích (hoặc có khả năng gây hiểu lầm), vì như bạn đã nói, bạn đang hành động trên các thư mục. Câu trả lời của Shawn J. Goff sẽ cung cấp một giải pháp hiệu quả và hiệu quả hơn cho vấn đề của bạn.
lsđầu ra , bạn không nên đọc đầu ra trong một forvòng lặp , bạn nên sử dụng $()thay vì `` và Sử dụng thêm Báo giá ™ . Tôi chắc chắn @CoverosGene có nghĩa là tốt, nhưng điều này thật tồi tệ.
lslà ls -1 | while read line; do stuff; done. Ít nhất cái đó sẽ không phá vỡ cho khoảng trắng.
mv -vbạn không cầnecho "moved $x -> $t"
Sử dụng đầu ra của lsđể có được tên tệp là một ý tưởng tồi . Nó có thể dẫn đến các kịch bản trục trặc và thậm chí nguy hiểm. Điều này là do tên tệp có thể chứa bất kỳ ký tự nào ngoại trừ /và nullký tự đó và lskhông sử dụng một trong hai ký tự đó làm dấu phân cách, vì vậy nếu tên tệp có khoảng trắng hoặc dòng mới, bạn sẽ nhận được kết quả không mong muốn.
Có hai cách lặp lại rất tốt trên các tập tin. Ở đây, tôi đã sử dụng đơn giản echođể chứng minh làm một cái gì đó với tên tệp; bạn có thể sử dụng bất cứ điều gì, mặc dù.
Đầu tiên là sử dụng các tính năng toàn cầu hóa của shell.
for dir in */; do
echo "$dir"
done
Shell mở rộng */thành các đối số riêng biệt mà forvòng lặp đọc; ngay cả khi có một khoảng trắng, dòng mới hoặc bất kỳ ký tự lạ nào khác trong tên tệp, forsẽ thấy mỗi tên hoàn chỉnh là một đơn vị nguyên tử; nó không phân tích danh sách theo bất kỳ cách nào.
Nếu bạn muốn đi đệ quy vào thư mục con, thì điều này sẽ không làm trừ khi vỏ của bạn có một số tính năng globbing mở rộng (chẳng hạn như bash's globstar. Nếu vỏ của bạn không có tính năng này, hoặc nếu bạn muốn đảm bảo rằng kịch bản của bạn sẽ làm việc trên nhiều hệ thống khác nhau, thì tùy chọn tiếp theo là sử dụng find.
find . -type d -exec echo '{}' \;
Ở đây, findlệnh sẽ gọi echovà truyền cho nó một đối số của tên tệp. Nó làm điều này một lần cho mỗi tập tin nó tìm thấy. Như với ví dụ trước, không có phân tích danh sách tên tệp; thay vào đó, một fileneame được gửi hoàn toàn như một đối số.
Cú pháp của -execđối số có vẻ hơi buồn cười. findlấy đối số đầu tiên sau -execvà coi đó là chương trình để chạy và mọi đối số tiếp theo, nó sẽ là đối số để truyền cho chương trình đó. Có hai đối số đặc biệt -execcần xem. Thứ nhất là {}; đối số này được thay thế bằng một tên tệp mà các phần trước đó findtạo ra. Cái thứ hai là ;, cho phép findbiết đây là phần cuối của danh sách các đối số được truyền cho chương trình; findcần điều này bởi vì bạn có thể tiếp tục với nhiều đối số được dự định findvà không dành cho chương trình exec'd. Lý do \là vỏ cũng xử lý;đặc biệt - nó đại diện cho sự kết thúc của một lệnh, vì vậy chúng ta cần phải thoát nó để shell cung cấp cho nó như là một đối số findthay vì tiêu thụ nó cho chính nó; một cách khác để có được vỏ không xử lý nó đặc biệt là đặt nó trong dấu ngoặc kép: ';'hoạt động cũng như \;cho mục đích này.
find. Có phép thuật có thể được thực hiện, và thậm chí những hạn chế nhận thức được -execcó thể được thực hiện xung quanh. Các -print0tùy chọn cũng có giá trị để sử dụng với xargs.
Đối với các tệp có khoảng trắng trong bạn sẽ phải đảm bảo trích dẫn biến như:
for i in $(ls); do echo "$i"; done;
hoặc, bạn có thể thay đổi biến môi trường phân tách trường đầu vào (IFS):
IFS=$'\n';for file in $(ls); do echo $i; done
Cuối cùng, tùy thuộc vào những gì bạn đang làm, bạn thậm chí có thể không cần ls:
for i in *; do echo "$i"; done;
\nlà không đủ. Không bao giờ là một ý tưởng tốt để đề xuất phân tích đầu ra ls.
Nếu bạn đã cài đặt GNU Parallel http://www.gnu.org/software/pool/, bạn có thể làm điều này:
ls | parallel echo {} is in this dir
Để đổi tên tất cả .txt thành .xml:
ls *.txt | parallel mv {} {.}.xml
Xem video giới thiệu về GNU Parallel để tìm hiểu thêm: http://www.youtube.com/watch?v=OpaiGYxkSuQ
Tại sao không đặt IFS thành một dòng mới, sau đó nắm bắt đầu ra của lsmột mảng? Đặt IFS thành dòng mới sẽ giải quyết các vấn đề với các ký tự vui nhộn trong tên tệp; sử dụng lscó thể tốt bởi vì nó có một cơ sở sắp xếp tích hợp.
(Trong thử nghiệm, tôi đã gặp sự cố khi đặt IFS thành \nnhưng cài đặt nó thành công việc backspace dòng mới, như được đề xuất ở nơi khác ở đây):
Ví dụ: (giả sử lsmẫu tìm kiếm mong muốn được truyền vào $1):
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
FILES=($(/bin/ls "$1"))
for AFILE in ${FILES[@]}
do
... do something with a file ...
done
IFS=$SAVEIFS
Điều này đặc biệt tiện dụng trên OS X, ví dụ, để chụp danh sách các tệp được sắp xếp theo ngày tạo (cũ nhất đến mới nhất), lslệnh này là ls -t -U -r.
\nlà không đủ. Các giải pháp hợp lệ duy nhất sử dụng vòng lặp for với mở rộng tên đường dẫn hoặc find.
Đây là cách tôi làm, nhưng có lẽ có nhiều cách hiệu quả hơn.
ls > filelist.txt
while read filename; do echo filename: "$filename"; done < filelist.txt
ls | while read i; do echo filename: $i; done