Làm thế nào tôi có thể làm phẳng một thư mục theo định dạng sau?
Trước: ./aaa/bbb/ccc.png
Sau: ./aaa-bbb-ccc.png
./foo/bar/baz.pngvà ./foo-bar-baz.pngcả hai tồn tại. Tôi cho rằng bạn không muốn thay thế cái sau bằng cái trước?
Làm thế nào tôi có thể làm phẳng một thư mục theo định dạng sau?
Trước: ./aaa/bbb/ccc.png
Sau: ./aaa-bbb-ccc.png
./foo/bar/baz.pngvà ./foo-bar-baz.pngcả hai tồn tại. Tôi cho rằng bạn không muốn thay thế cái sau bằng cái trước?
Câu trả lời:
Cảnh báo: Tôi đã gõ hầu hết các lệnh này trực tiếp trong trình duyệt của mình. Caveat lector.
Với zsh và zmv :
zmv -o -i -Qn '(**/)(*)(D)' '${1//\//-}$2'
Giải thích: Mẫu **/*phù hợp với tất cả các tệp trong thư mục con của thư mục hiện tại, theo cách đệ quy (nó không khớp với các tệp trong thư mục hiện tại, nhưng chúng không cần phải đổi tên). Hai cặp dấu ngoặc đầu tiên là các nhóm có thể được giới thiệu là $1và $2trong văn bản thay thế. Cặp dấu ngoặc đơn cuối cùng thêm D vòng loại toàn cầu để các tệp chấm không bị bỏ qua. -o -icó nghĩa là chuyển -itùy chọn để mvbạn được nhắc nếu một tệp hiện có sẽ bị ghi đè.
Chỉ với các công cụ POSIX:
find . -depth -exec sh -c '
for source; do
case $source in ./*/*)
target="$(printf %sz "${source#./}" | tr / -)";
mv -i -- "$source" "${target%z}";;
esac
done
' _ {} +
Giải thích: casecâu lệnh bỏ qua thư mục hiện tại và thư mục con cấp cao nhất của thư mục hiện tại. targetchứa tên tệp nguồn ( $0) với phần đầu ./bị tước và tất cả các dấu gạch chéo được thay thế bằng dấu gạch ngang, cộng với phần cuối cùng z. Cuối cùng zlà ở đó trong trường hợp tên tệp kết thúc bằng một dòng mới: nếu không thì lệnh thay thế sẽ loại bỏ nó.
Nếu bạn findkhông hỗ trợ -exec … +(OpenBSD, tôi đang nhìn bạn):
find . -depth -exec sh -c '
case $0 in ./*/*)
target="$(printf %sz "${0#./}" | tr / -)";
mv -i -- "$0" "${target%z}";;
esac
' {} \;
Với bash (hoặc ksh93), bạn không cần phải gọi lệnh bên ngoài để thay thế dấu gạch chéo bằng dấu gạch ngang, bạn có thể sử dụng mở rộng tham số ksh93 với cấu trúc thay thế chuỗi ${VAR//STRING/REPLACEMENT}:
find . -depth -exec bash -c '
for source; do
case $source in ./*/*)
source=${source#./}
target="${source//\//-}";
mv -i -- "$source" "$target";;
esac
done
' _ {} +
for sourceví dụ bỏ lỡ tệp / thư mục được trình bày đầu tiên ... Cuối cùng tôi đã tìm ra lý do tại sao bạn (thường) đã sử dụng _như $0:) ... và cảm ơn vì những câu trả lời tuyệt vời. Tôi học được rất nhiều từ họ.
Mặc dù đã có câu trả lời tốt ở đây, tôi thấy điều này trực quan hơn, trong bash:
find aaa -type f -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); mv "{}" "$new"' \;
Thư mục aaahiện tại của bạn ở đâu Các tệp mà nó chứa sẽ được chuyển đến thư mục hiện tại (chỉ để lại các thư mục trống trong aaa). Hai cuộc gọi để trxử lý cả thư mục và dấu cách (không có logic để thoát dấu gạch chéo - một bài tập cho người đọc).
Tôi xem xét điều này trực quan hơn và tương đối dễ dàng để điều chỉnh. Tức là tôi có thể thay đổi các findtham số nếu nói rằng tôi chỉ muốn tìm các tệp .png. Tôi có thể thay đổi các trcuộc gọi, hoặc thêm nhiều hơn khi cần thiết. Và tôi có thể thêm echotrước mvnếu tôi muốn kiểm tra trực quan rằng nó sẽ làm đúng hay không (sau đó lặp lại mà không cần thêm echonếu mọi thứ có vẻ đúng:
find aaa -name \*.png -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); echo mv "{}" "$new"' \;
Cũng lưu ý rằng tôi đang sử dụng find aaa... và không find .... hoặc find aaa/... vì hai phần sau sẽ để lại các tạo phẩm vui nhộn trong tên tệp cuối cùng.
find . -mindepth 2 -type f -name '*' |
perl -l000ne 'print $_; s/\//-/g; s/^\.-/.\// and print' |
xargs -0n2 mv
Lưu ý: điều này sẽ không hoạt động cho tên tệp có chứa \n.
Điều này, tất nhiên chỉ di chuyển các loại ftệp ...
Các xung đột tên duy nhất sẽ là từ các tệp có sẵn trong pwd
Đã thử nghiệm với tập hợp con cơ bản này
rm -fr junk
rm -f junk*hello*
mkdir -p junk/junkier/junkiest
touch 'hello hello'
touch 'junk/hello hello'
touch 'junk/junkier/hello hello'
touch 'junk/junkier/junkiest/hello hello'
Kết quả là
./hello hello
./junk-hello hello
./junk-junkier-hello hello
./junk-junkier-junkiest-hello hello
Đây là một lsphiên bản .. bình luận hoan nghênh. Nó hoạt động cho các tên tập tin như thế này
"j1ळ\n\001\n/j2/j3/j4/\nh h\n", nhưng tôi thực sự quan tâm để biết bất kỳ cạm bẫy. Nó giống như một bài tập trong "những kẻ ác ý lsthực sự có thể làm điều đó (mạnh mẽ) không?"
ls -AbQR1p * | # paths in "quoted" \escaped format
sed -n '/":$/,${/.[^/]$/p}' | # skip files in pwd, blank and dir/ lines
{ bwd="$PWD"; while read -n1; # save base dir strip leading "quote
read -r p; do # read path
printf -vs "${p%\"*}" # strip trailing quote"(:)
[[ $p == *: ]] && { # this is a directory
cd "$bwd/$s" # change into new directory
rnp="${s//\//-}"- # relative name prefix
} || mv "$s" "$bwd/$rnp$s"
done; }
Chỉ cần đặt tên tệp + đường dẫn đến trlệnh.tr <replace> <with>
for FILE in `find aaa -name "*.png"`
do
NEWFILE=`echo $FILE | tr '/' '-'`
cp -v $FILE $NEWFILE
done
cpdòng để cp -v "$FILE" "$NEWFILE"giúp đỡ? (Ngoài ra, bạn không nên chỉ sử dụng các tệp png.)
cpdòng là không đủ. Toàn bộ cách tiếp cận của phân tích cú pháp findđầu ra với một forvòng lặp là thiếu sót. Các cách an toàn duy nhất để sử dụng findlà với -exec, hoặc -print0nếu có sẵn (ít di động hơn -exec). Tôi muốn đề xuất một giải pháp thay thế mạnh mẽ hơn nhưng câu hỏi của bạn chưa được chỉ định rõ ràng vào lúc này.
An toàn cho không gian trong tên tệp:
#!/bin/bash
/bin/find $PWD -type f | while read FILE
do
_new="${FILE//\//-}"
_new="${_new:1}"
#echo $FILE
#echo $_new
/bin/mv "$FILE" "$_new"
done
Ran của tôi với cpthay vì mvlý do rõ ràng, nhưng nên sản xuất như vậy.
type find-> find is /usr/bin/findtrên máy của tôi. Sử dụng PATHnhư một tên biến trong một kịch bản là một ý tưởng khủng khiếp. Ngoài ra, tập lệnh của bạn mang tên tập tin có dấu cách hoặc dấu gạch chéo ngược, vì bạn sử dụng sai readlệnh (tìm kiếm trang web này IFS read -r).
find is hashed (/bin/find), có liên quan như thế nào? Phân phối khác nhau là khác nhau?
PATHbiến môi trường, đây là điều mà mọi hệ thống Unix đều sử dụng. Nếu bạn viết /bin/findthì tập lệnh của bạn thực sự bị hỏng trên mọi hệ thống (Gilles, của tôi và có lẽ là của OP) không có /bin/find(ví dụ: b / c nó nằm trong /usr/bin/find). Toàn bộ điểm của PATHbiến môi trường là làm cho điều này không thành vấn đề.
$(pwd)đã có sẵn trong một biến : $PWD. Nhưng bạn không được sử dụng một đường dẫn tuyệt đối ở đây: nếu tập lệnh của bạn được gọi từ, giả sử /home/tim, nó sẽ cố gắng đổi tên /home/tim/some/paththành /home-tim-some-path.