Flatten Directory nhưng giữ nguyên tên thư mục trong tên tệp mới


10

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


PS: Vui lòng cũng tính đến tên tệp có các ký tự lẻ (ví dụ: khoảng trắng)
Nathan JB

3
Bạn cũng cần xác định cách bạn muốn xử lý trường hợp có thể xảy ra ở đâu ./foo/bar/baz.png./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?
jw013

Trong trường hợp của tôi, nó không liên quan, nhưng câu trả lời thực sự cần phải giải thích cho điều đó để người khác có thể dựa vào nó! Cảm ơn!
Nathan JB

Câu trả lời:


7

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$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
' _ {} +

Các 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ọ.
Peter.O

Lưu ý rằng ví dụ zmv chỉ chạy khô: nó in ra các lệnh di chuyển nhưng nó không thực thi chúng. Để thực sự thực thi các lệnh đó, loại bỏ n trong -Qn.
Peter

5

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.


Cảm ơn vì một lớp lót này - nó hoạt động tốt với tôi khi tôi thực hiện nó từ bên ngoài thư mục (đó chính xác là những gì bạn nói phải làm). Khi tôi cố gắng thực hiện từ bên trong thư mục (hy vọng tránh được tên thư mục gốc được thêm vào), tất cả các tệp "biến mất". Tôi đã biến chúng thành các tập tin ẩn trong cùng thư mục đó.
dgig

2
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

0

Đâ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; }

-1

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

1
Điều này không xử lý tên tập tin kỳ lạ một cách an toàn. Không gian sẽ phá vỡ nó (trong số những thứ khác).
jw013

Thoạt nhìn, đây là một giải pháp dễ đọc, dễ đọc nhưng @ jw013 đã đúng, nó sẽ không xử lý tốt các không gian. Sẽ thay đổi 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.)
Nathan JB

2
@ NathanJ.Brauer Thêm dấu ngoặc kép vào 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.
jw013

-2

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.


3
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).
Gilles 'SO- ngừng trở thành ác quỷ'

find is hashed (/bin/find), có liên quan như thế nào? Phân phối khác nhau là khác nhau?
Tim

Tôi nên tránh xa điều này, mồi kền kền.
Tim

@Tim Bạn luôn có thể xóa câu trả lời để lấy lại đại diện của mình (và của tôi cũng vậy vì chi phí rep cho downvote). Các đường dẫn mã hóa cứng vào các tập lệnh dường như cho thấy rằng bạn đang thiếu điểm của 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 đề.
jw013

Nhân tiệ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.
Gilles 'SO- ngừng trở thành ác quỷ'
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.