Có ai có một kịch bản shell shell để làm một cái gì đó với ls
mộ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 ls
mộ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..done
vòng lặp:
for f in *; do
echo "File -> $f"
done
Bạn có thể thay thế *
bằng *.txt
hoặ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 do
vò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 ( $f
trong ví dụ trên). Bạn có thể echo
nó 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 .xml
tệ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
for
cá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 for
vò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ệ.
ls
là ls -1 | while read line; do stuff; done
. Ít nhất cái đó sẽ không phá vỡ cho khoảng trắng.
mv -v
bạ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à null
ký tự đó và ls
khô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à for
vò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, for
sẽ 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, find
lệnh sẽ gọi echo
và 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. find
lấy đối số đầu tiên sau -exec
và 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 -exec
cầ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 đó find
tạo ra. Cái thứ hai là ;
, cho phép find
biết đây là phần cuối của danh sách các đối số được truyền cho chương trình; find
cầ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 find
và 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ố find
thay 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 -exec
có thể được thực hiện xung quanh. Các -print0
tù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;
\n
là 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 ls
mộ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 ls
có 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 \n
như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ử ls
mẫ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), ls
lệnh này là ls -t -U -r
.
\n
là 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