Câu trả lời:
Trong Bash, bạn có thể làm điều đó bằng cách bật extglob
tùy chọn, như thế này (tất nhiên thay thế ls
bằng cp
và thêm thư mục đích)
~/foobar> shopt extglob
extglob off
~/foobar> ls
abar afoo bbar bfoo
~/foobar> ls !(b*)
-bash: !: event not found
~/foobar> shopt -s extglob # Enables extglob
~/foobar> ls !(b*)
abar afoo
~/foobar> ls !(a*)
bbar bfoo
~/foobar> ls !(*foo)
abar bbar
Sau này bạn có thể vô hiệu hóa extglob với
shopt -u extglob
f
, ngoại trừ foo
?
Các extglob
tùy chọn vỏ mang đến cho bạn phù hợp với mô hình mạnh mẽ hơn trong dòng lệnh.
Bạn bật nó lên shopt -s extglob
và tắt nó đi shopt -u extglob
.
Trong ví dụ của bạn, ban đầu bạn sẽ làm:
$ shopt -s extglob
$ cp !(*Music*) /target_directory
Các toán tử bing toàn cầu đã kết thúc ext có sẵn (trích từ ):man bash
Nếu tùy chọn shell extglob được bật bằng cách sử dụng nội dung shopt, một số toán tử khớp mẫu mở rộng được nhận dạng. Danh sách mẫu là danh sách một hoặc nhiều mẫu được phân tách bằng dấu |. Các mẫu tổng hợp có thể được hình thành bằng cách sử dụng một hoặc nhiều mẫu phụ sau:
- ? (danh sách mẫu)
Khớp 0 hoặc một lần xuất hiện của các mẫu đã cho- * (danh sách mẫu)
Khớp 0 hoặc nhiều lần xuất hiện của các mẫu đã cho- + (danh sách mẫu)
Khớp với một hoặc nhiều lần xuất hiện của các mẫu đã cho- @ (danh sách mẫu)
Phù hợp với một trong các mẫu đã cho- ! (danh sách mẫu)
Phù hợp với mọi thứ trừ một trong các mẫu đã cho
Vì vậy, ví dụ, nếu bạn muốn liệt kê tất cả các tệp trong thư mục hiện tại không phải .c
hoặc .h
tệp, bạn sẽ làm:
$ ls -d !(*@(.c|.h))
Tất nhiên, hoạt động hả hê vỏ bình thường, vì vậy ví dụ cuối cùng cũng có thể được viết là:
$ ls -d !(*.[ch])
.c
hoặc .h
là một thư mục.
D
, nhưng không phải là nội dung của các thư mục con có thể có trong thư mục D
.
Không phải trong bash (mà tôi biết), nhưng:
cp `ls | grep -v Music` /target_directory
Tôi biết đây không phải là chính xác những gì bạn đang tìm kiếm, nhưng nó sẽ giải quyết ví dụ của bạn.
Nếu bạn muốn tránh chi phí mem khi sử dụng lệnh exec, tôi tin rằng bạn có thể làm tốt hơn với xargs. Tôi nghĩ rằng sau đây là một thay thế hiệu quả hơn để
find foo -type f ! -name '*Music*' -exec cp {} bar \; # new proc for each exec
find . -maxdepth 1 -name '*Music*' -prune -o -print0 | xargs -0 -i cp {} dest/
Trong bash, một thay thế cho shopt -s extglob
là GLOBIGNORE
biến . Nó không thực sự tốt hơn, nhưng tôi thấy dễ nhớ hơn.
Một ví dụ có thể là những gì người đăng ban đầu muốn:
GLOBIGNORE="*techno*"; cp *Music* /only_good_music/
Khi hoàn thành, unset GLOBIGNORE
để có thể rm *techno*
trong thư mục nguồn.
Bạn cũng có thể sử dụng một for
vòng lặp khá đơn giản :
for f in `find . -not -name "*Music*"`
do
cp $f /target/dir
done
-maxdepth 1
cho không đệ quy?
find
trong backticks sẽ phá vỡ theo những cách khó chịu nếu nó tìm thấy bất kỳ tên tệp không cần thiết.
Sở thích cá nhân của tôi là sử dụng grep và lệnh while. Điều này cho phép một người viết các kịch bản mạnh mẽ nhưng có thể đọc được, đảm bảo rằng bạn sẽ thực hiện chính xác những gì bạn muốn. Ngoài ra, bằng cách sử dụng lệnh echo, bạn có thể thực hiện chạy khô trước khi thực hiện thao tác thực tế. Ví dụ:
ls | grep -v "Music" | while read filename
do
echo $filename
done
sẽ in ra các tập tin mà bạn sẽ sao chép. Nếu danh sách đúng, bước tiếp theo chỉ đơn giản là thay thế lệnh echo bằng lệnh sao chép như sau:
ls | grep -v "Music" | while read filename
do
cp "$filename" /target_directory
done
bash
bạn có thể sử dụng while IFS='' read -r filename
, nhưng sau đó các dòng mới vẫn là một vấn đề. Nói chung, tốt nhất là không sử dụng ls
để liệt kê các tập tin; công cụ như find
là phù hợp hơn nhiều.
for file in *; do case ${file} in (*Music*) ;; (*) cp "${file}" /target_directory ; echo ;; esac; done
Một thủ thuật tôi đã không nhìn thấy trên đây được nêu ra mà không sử dụng extglob
, find
hoặc grep
là để điều trị hai danh sách tập tin như bộ và "diff" chúng bằng comm
:
comm -23 <(ls) <(ls *Music*)
comm
được ưa thích hơn diff
bởi vì nó không có thêm hành trình.
Lợi nhuận này tất cả các yếu tố của bộ 1, ls
, đó là không còn ở set 2, ls *Music*
. Điều này đòi hỏi cả hai bộ phải được sắp xếp để hoạt động đúng. Không có vấn đề gì đối với ls
việc mở rộng toàn cầu, nhưng nếu bạn đang sử dụng một cái gì đó như thế find
, hãy chắc chắn gọi sort
.
comm -23 <(find . | sort) <(find . | grep -i '.jpg' | sort)
Có khả năng hữu ích.
comm -23 <(ls) exclude_these.list
Một giải pháp cho điều này có thể được tìm thấy với find.
$ mkdir foo bar
$ touch foo/a.txt foo/Music.txt
$ find foo -type f ! -name '*Music*' -exec cp {} bar \;
$ ls bar
a.txt
Tìm có khá nhiều tùy chọn, bạn có thể nhận được khá cụ thể về những gì bạn bao gồm và loại trừ.
Chỉnh sửa: Adam trong các ý kiến lưu ý rằng điều này là đệ quy. tìm tùy chọn mindepth và maxdepth có thể hữu ích trong việc kiểm soát điều này.
find -maxdepth 1 -not -name '*Music*'
/ target_directory
Các tác phẩm sau đây liệt kê tất cả *.txt
các tệp trong thư mục hiện tại, ngoại trừ những tệp bắt đầu bằng một số.
Này hoạt động trong bash
, dash
, zsh
và tất cả vỏ tương thích khác POSIX.
for FILE in /some/dir/*.txt; do # for each *.txt file
case "${FILE##*/}" in # if file basename...
[0-9]*) continue ;; # starts with digit: skip
esac
## otherwise, do stuff with $FILE here
done
Trong dòng một, mẫu /some/dir/*.txt
sẽ khiến for
vòng lặp lặp lại trên tất cả các tệp /some/dir
có tên kết thúc bằng .txt
.
Trong dòng hai, một câu lệnh tình huống được sử dụng để loại bỏ các tệp không mong muốn. - ${FILE##*/}
Biểu thức loại bỏ bất kỳ thành phần tên thư mục hàng đầu nào khỏi tên tệp (ở đây /some/dir/
) để các phần tử có thể khớp với chỉ tên cơ sở của tệp. (Nếu bạn chỉ loại bỏ tên tệp dựa trên hậu tố, bạn có thể rút ngắn tên này thành $FILE
thay thế.)
Trong dòng ba, tất cả các tệp khớp với case
mẫu [0-9]*
) sẽ bị bỏ qua ( continue
câu lệnh nhảy sang lần lặp tiếp theo của for
vòng lặp). - Nếu bạn muốn, bạn có thể làm điều gì đó thú vị hơn ở đây, ví dụ như bỏ qua tất cả các tệp không bắt đầu bằng một chữ cái (một Z z) bằng cách sử dụng [!a-z]*
hoặc bạn có thể sử dụng nhiều mẫu để bỏ qua một số loại tên tệp, ví dụ như [0-9]*|*.bak
bỏ qua các tệp cả hai .bak
tệp và các tệp không bắt đầu bằng số.
*.txt
thay vì chỉ *
). Đã sửa bây giờ.
điều này sẽ làm điều đó không bao gồm chính xác 'Âm nhạc'
cp -a ^'Music' /target
cái này và cái kia để loại trừ những thứ như Âm nhạc? * hoặc *? Âm nhạc
cp -a ^\*?'complete' /target
cp -a ^'complete'?\* /target
cp
trang hướng dẫn trên hệ điều hành MacOS có một -a
lựa chọn nhưng nó làm điều gì đó hoàn toàn khác nhau. Nền tảng nào hỗ trợ điều này?
ls /dir/*/!(base*)