Hạ tất cả các thư mục trong một thư mục


12

Tôi muốn viết thường tên của mỗi thư mục trong một thư mục. Với những lệnh nào tôi có thể làm điều đó?

Câu trả lời:


10

Tất cả các thư mục ở một cấp độ, hoặc đệ quy?

Zsh

Ở một cấp độ:

autoload zmv
zmv -o-i -Q 'root/(*)(/)' 'root/${1:l}'

Đệ quy:

zmv -o-i -Q 'root/(**/)(*)(/)' 'root/$1${2:l}'

Giải thích: zmvđổi tên các tệp khớp với mẫu theo văn bản thay thế đã cho. -o-ichuyển -itùy chọn cho mỗi mvlệnh dưới mui xe (xem bên dưới). Trong văn bản thay thế, $1, $2, vv, là những nhóm trong ngoặc đơn liên tiếp trong mẫu. **có nghĩa là tất cả các thư mục (phụ) *, đệ quy. Trận chung kết (/)không phải là một nhóm được ngoặc đơn mà là một vòng loại toàn cầu có nghĩa là chỉ khớp với các thư mục. ${2:l}chuyển đổi $2thành chữ thường.

Di động

Ở một cấp độ:

for x in root/*/; do mv -i "$x" "$(printf %s "$x" | tr '[:upper:]' '[:lower:]')"; done

Cuối cùng /hạn chế sự phù hợp với các thư mục và mv -ilàm cho nó yêu cầu xác nhận trong trường hợp va chạm. Hủy bỏ -iđể ghi đè trong trường hợp va chạm và sử dụng yes n | for …. không được nhắc và không thực hiện bất kỳ đổi tên nào sẽ va chạm.

Đệ quy:

find root/* -depth -type d -exec sh -c '
    t=${0%/*}/$(printf %s "${0##*/}" | tr "[:upper:]" "[:lower:]");
    [ "$t" = "$0" ] || mv -i "$0" "$t"
' {} \;

Việc sử dụng -depthđảm bảo rằng các thư mục lồng nhau sâu được xử lý trước tổ tiên của chúng. Việc xử lý tên dựa vào đó là một /; nếu bạn muốn gọi hoạt động trong thư mục hiện tại, hãy sử dụng ./*(điều chỉnh kịch bản shell để đối phó .hoặc *để lại như một bài tập cho người đọc).

Đổi tên

Ở đây tôi sử dụng tập lệnh đổi tên Perl mà Debian và Ubuntu /usr/bin/prenamecung cấp (thường cũng có sẵn rename). Ở một cấp độ:

rename 's!/([^/]*/?)$!\L/$1!' root/*/

Đệ quy, với bash ≥4 hoặc zsh:

shopt -s globstar  # only in bash
rename 's!/([^/]*/?)$!\L/$1!' root/**/*/

Đệ quy, di chuyển:

find root -depth -type d -exec rename -n 's!/([^/]*/?)$!\L/$1!' {} +

Ít nhất trên OS X, điều này sẽ thất bại nếu bất kỳ thư mục nào đã được viết thường: mv -i a ađưa ra "mv: đổi tên a thành a / a: Đối số không hợp lệ".
Janus

@Janus: Đúng vậy, bạn nhận được một thông báo lỗi, điều này thật xấu xí (mặc dù vô hại ở dòng lệnh). Nhưng dù sao tôi cũng nên sử dụng zmv, trong đó xử lý trường hợp này.
Gilles 'SO- ngừng trở nên xấu xa'

Sau đó tôi tìm thấy về -execdirmà là awesome: unix.stackexchange.com/questions/5412/... tôi sau đó phát hiện ra rằng nó có một số PATHđiên rồ và rất buồn :-(
Ciro Santilli新疆改造中心法轮功六四事件

4

Không có một lệnh nào sẽ làm điều đó, nhưng bạn có thể làm một cái gì đó như thế này:

for fd in */; do
  #get lower case version
  fd_lower=$(printf %s "$fd" | tr A-Z a-z)
  #if it wasn't already lowercase, move it.
  [ "$fd" != "$fd_lower" ] && mv "$fd" "$fd_lower"
done

Nếu bạn cần nó mạnh mẽ, bạn nên tính đến khi đã có hai thư mục chỉ khác nhau trong trường hợp.

Như một lớp lót:

for fd in */; do fd_lower=$(printf %s "$fd" | tr A-Z a-z) && [ "$fd" != "$fd_lower" ] && mv "$fd" "$fd_lower"; done

Điều này xuất hiện giữa chừng trong bài viết của tôi về đề xuất (về cơ bản là giống nhau):for file in * ; do if [ -d "$file" ] ; then dest="$(echo $file | sed y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/)" ; [ "$dest" != "$file" ] && mv "$file" "$dest" ; fi ; done
frabjous

Tôi đã cố gắng giải quyết nó find -type d, nhưng không thể hiểu được
Michael Mrozek

1
Bản năng của tôi sẽ phải làm for fd in */;, do đó tránh sự cần thiết phải kiểm tra nếu đó là một thư mục, nhưng tôi không biết bản năng này có phải là một thứ tốt hay không.
Steven D

Đúng, cho ... * / sẽ tốt hơn.
Shawn J. Goff

1
@Shawn: trđã hy vọng một phạm vi, vì vậy tr '[A-Z]' '[a-z]'dịch [đến []để ]trong đi qua. Điều này là vô ích nhưng vô hại; tuy nhiên không có dấu ngoặc kép, shell sẽ mở rộng dấu ngoặc nếu có một tệp có tên một chữ cái in hoa trong thư mục hiện tại.
Gilles 'SO- ngừng trở nên xấu xa'

0

Tôi đã coi đây là một thử thách một lót :) Đầu tiên, hãy thiết lập một trường hợp thử nghiệm:

$ for d in foo Bar eVe; do mkdir -p dcthis/$d; touch dcthis/a${d}.txt; done
$ ls dcthis/
Bar     aBar.txt    aeVe.txt    afoo.txt    eVe     foo

Tôi sử dụng findđể phát hiện các thư mục với các chữ cái viết hoa và sau đó viết thường chúng . Đoán sử dụng là một chút hack-ish, nhưng đầu tôi luôn nổ tung khi tôi cố gắng thoát khỏi mọi thứ trực tiếp.sh -c 'mv {} echo {} | tr [:upper:] [:lower:]'sh -cfind

$ (cd dcthis && find . -maxdepth 1 -type d -path '*[A-Z]*' -exec sh -c 'mv {} `echo {} | tr [:upper:] [:lower:]`' \;)
$ ls dcthis/
aBar.txt    aeVe.txt    afoo.txt    bar     eve     foo

Được cảnh báo: Giải pháp này không kiểm tra xem việc hạ xuống có dẫn đến va chạm hay không!


kiểm tra va chạm rất dễ dàng : mv -i. Một vấn đề lớn hơn là bạn chưa sử dụng trích dẫn thích hợp, vì vậy lệnh của bạn sẽ thất bại nếu có các ký tự đặc biệt (khoảng trắng hoặc \[*?) ở bất kỳ đâu trong tên. Không có điểm sử dụng findtrừ khi đệ quy, và sau đó bạn cần find -depth, và -pathphải -name. Xem câu trả lời của tôi cho các ví dụ làm việc.
Gilles 'SO- ngừng trở nên xấu xa'

@Giles. Cảm ơn! Tôi thấy bạn mv -isau khi viết này. Điểm hay với trích dẫn ...
Janus

0

find -execdir| đổi tên

Đây sẽ là cách tốt nhất để làm điều đó nếu nó không vì sự điên rồ của đường dẫn tương đối, vì nó tránh cho Perl regex fu chỉ hành động trên tên cơ sở:

PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
  find a -depth -execdir rename 's/(.*)/\L$1/' '{}' \;

-execdirđầu tiên cdvào thư mục trước khi chỉ thực hiện trên tên cơ sở.

Thật không may, tôi không thể thoát khỏi PATHphần hack đó , find -execdirtừ chối làm bất cứ điều gì nếu bạn có một đường dẫn tương đối trong PATH...: https://askubfox.com/questions/621132/why-USE-the-execdir-action- is-insecure-for-thư mục-which-is-in-the-path / 1109378 # 1109378

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.