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.png
và ./foo-bar-baz.png
cả 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.png
và ./foo-bar-baz.png
cả 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à $1
và $2
trong 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 -i
có nghĩa là chuyển -i
tùy chọn để mv
bạ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: case
câ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. target
chứ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 z
là ở đó 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 find
khô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 source
ví 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 aaa
hiệ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 để tr
xử 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 find
tham 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 tr
cuộc gọi, hoặc thêm nhiều hơn khi cần thiết. Và tôi có thể thêm echo
trước mv
nế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 echo
nế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 f
tệ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 ls
phiê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 ý ls
thự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 tr
lệnh.tr <replace> <with>
for FILE in `find aaa -name "*.png"`
do
NEWFILE=`echo $FILE | tr '/' '-'`
cp -v $FILE $NEWFILE
done
cp
dò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.)
cp
dò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 for
vòng lặp là thiếu sót. Các cách an toàn duy nhất để sử dụng find
là với -exec
, hoặc -print0
nế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 cp
thay vì mv
lý do rõ ràng, nhưng nên sản xuất như vậy.
type find
-> find is /usr/bin/find
trên máy của tôi. Sử dụng PATH
như 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 read
lệ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?
PATH
biế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/find
thì 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 PATH
biế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/path
thành /home-tim-some-path
.