Xóa dấu cách, dấu gạch nối và dấu gạch dưới trong tên tệp?


10

Một lệnh tốt để xóa khoảng trắng, dấu gạch ngang và dấu gạch dưới khỏi tất cả các tệp trong một thư mục hoặc các tệp được chọn là gì?

Tôi sử dụng lệnh sau với Tác vụ tùy chỉnh Thunar để tạo tên tập tin:

for file in %N; do mv "$file" "$(echo "$file" | tr -s ' ' | tr ' A-Z' '-a-z' | tr -s '-' | tr -c '[:alnum:][:cntrl:].' '-')"; done

Nhưng lệnh đó chỉ thay thế khoảng trắng bằng dấu gạch ngang / dấu gạch ngang và ký tự viết thường.

Tôi đã sử dụng lệnh sau trong thiết bị đầu cuối để xóa khoảng trắng khỏi hàng ngàn tên tệp trong một thư mục và nó hoạt động khá nhanh:

 rename "s/ //g" *

Một lần nữa, nó chỉ xóa các khoảng trắng, và không gạch nối / dấu gạch ngang và dấu gạch dưới là tốt.

Lý tưởng nhất là tôi không muốn bất kỳ khoảng trắng, dấu gạch ngang / dấu gạch ngang và dấu gạch dưới trong tên tệp của mình. Và sẽ thật tuyệt nếu lệnh có thể được sử dụng với Tác vụ tùy chỉnh Thunar trên các tệp được chọn.


2
Tôi lưu ý một vấn đề mà rất nhiều giải pháp được đề xuất, đó là không kiểm tra chính xác sự tồn tại của tên "mới" trước khi gửi tệp. Không làm điều đó có thể là nguồn tiềm năng của rất nhiều vấn đề.
mdpc

Có thể sửa đổi lệnh của John1024 để kiểm tra điều đó không?
dùng8547

@ user8547rename -i "s/[-_ ]//g" *
Sparhawk

Cảm ơn bạn Sparhawk. Ngẫu nhiên, đối với những người quan tâm đến việc sử dụng điều này như một hành động tùy chỉnh Thunar, lệnh cho Thunar là: cho tệp trong% N; làm mv "$ file" echo $file | sed -e 's/[ _-]//g'; xong
dùng8547

Câu trả lời:


11

Phiên bản renameđi kèm với perlgói hỗ trợ các biểu thức thông thường:

rename "s/[-_ ]//g" *

Ngoài ra,

rename -i "s/[-_ ]//g" *

Các -ilá cờ sẽ làm cho renamesử dụng chế độ tương tác, khiến nếu mục tiêu đã tồn tại, thay vì ghi đè âm thầm.

Đổi tên của Perl đôi khi được gọi prename.

Đổi tên của Perl so với đổi tên của linux-linux

Trên các hệ thống giống như Debian, việc đổi tên của perl dường như là mặc định và các lệnh trên sẽ chỉ hoạt động.

Trên một số bản phân phối, renametiện ích từ linux-linux là mặc định. Tiện ích này hoàn toàn không tương thích với Perl's rename.

  • Tất cả: Trước tiên, hãy kiểm tra xem Perl's renamecó sẵn dưới tên không prename.

  • Debian: Đổi tên của Perl phải là mặc định. Nó cũng có sẵn như prename. Các renamethực thi, tuy nhiên, là thuộc thẩm quyền của /etc/alternativesvà do đó có thể đã được thay đổi một cái gì đó khác nhau.

  • archlinux: Chạy pacman -S perl-renamevà lệnh có sẵn như perl-rename. Để có một tên thuận tiện hơn, tạo một bí danh. (Mũ chóp: Đục răng)

  • Mac OSX Theo câu trả lời này , renamecó thể được cài đặt trên OSX bằng homebrew qua:

    brew install rename 
  • Tải xuống trực tiếp: rename cũng có sẵn từ Perl Monks:

     wget 'http://www.perlmonks.org/?displaytype=displaycode;node_id=303814' -O rename

Tôi nghĩ rằng điều đó phụ thuộc vào những gì renamebạn đang nói về. Cái từ produc -linux -2.24.2-1.fc20.x86_64 không hỗ trợ các biểu thức thông thường.
Cristian Ciupitu

1
@CristianCiupitu Tôi vừa kiểm tra trang man cho phiên bản đổi tên mà bạn tìm thấy. Dựa trên các đối số, phiên bản renamemà OP đang sử dụng trông giống như perlphiên bản chứ không phải util-linuxphiên bản.
John1024

Đối với bản ghi, đây là renametrang hướng dẫn cho phiên bản linux-linux . Dù sao, bên cạnh ghi chú đó, điều quan trọng là OP đã có câu trả lời của anh ấy (và bạn là một người ủng hộ tôi :-D).
Cristian Ciupitu

@CristianCiupitu Cảm ơn bạn đã tìm thấy điều đó. Quay lại với bạn với +1.
John1024

1
@ John1024 archlinux, nhưng tôi đã tìm ra cách, chỉ cần đi pacman -S perl-renamethì tôi đoán bạn có thể bí danh.
Đục khoét

5

Tôi sẽ thay thế tất cả các trlệnh đó, bằng một sedlệnh thay thế, ví dụ:

for file in %N; do 
    mv "$file" "$(echo "$file" | sed 's/[ _-]//g')"
done

4

Không đếm mv, bạn không thực sự cần một quá trình bên ngoài cho điều này ở tất cả - bạn có thể loại chỉ poof họ.

ifsqz() ( LC_ALL=C sqz=$1
    isf() { [ -e "$1" ] || [ -L "$1" ] ; }  
    set -- * ; set -f
    for f do isf "$f" || break
    IFS=$sqz; set -- $f; IFS=
    isf "$*" || mv -- "$f" "$*"
    done
)

Tuy nhiên, điều đó có nghĩa là một mvlời gọi cho mỗi tệp, và vì vậy có lẽ renamelà tốt hơn. Mặc dù điều này sẽ làm việc đưa ra chỉ một POSIX mvtrong $PATHvà một lớp vỏ POSIX.

Vì vậy, tôi đã đưa ra một loại demo điên rồ cho việc này. Bộ kiểm tra được tạo như sau:

tee - - - - <<CGEN |\
dd cbs=90 conv=unblock |\
sed 'G;$!N'";s/^/touch -- '/;s/$/'/" |sh
$( #BEGIN CGEN
   LC_ALL=C
   i= n='"$((i=((i=i+1)==10||i==39||i==47)>0?(i+1):i))"'
   printf '%b -_   ---___'  $(
   IFS=0; eval \
       printf '"\\\\%04o\\\\%04o "' "$(
       printf "$n"' "$i" '%s $(
       printf %.252d
#END
))"))
CGEN

Ở nơi đầu tiên tôi sẽ là người đầu tiên thừa nhận rằng lệnh trên tạo ra kết quả có thể dễ dàng thu được hơn bằng các phương tiện khác. Nhưng các phương tiện khác có thể sẽ không thể hiện tốt như những gì có thể được thực hiện $IFSvà một chút trí tưởng tượng (bệnh?) .

Vì vậy, bit đầu tiên khá đơn giản:

  • tee bỏ ra 5 bản sao đầu vào của nó - di sản được gọi là CGEN

  • dd chặn đầu vào của nó bằng các dòng mới ở 90 byte mỗi khối và các đường dẫn đến ...

  • sedkết hợp 2 trong số các khối đó trên hai \nký tự ewline, 'trích dẫn một kết quả và thêm chuỗi touch --cho mỗi chu kỳ dòng trước khi chuyển sang ...

  • sh mà sau đó thực thi tất cả các đầu vào như các lệnh shell

Các #CGENchút mặc dù ... Vâng, một thời gian ngắn ...

  • phía dưới printfin 252 0s

  • tiếp theo từ cuối cùng nhận được 252 ''đối số chuỗi rỗng và cho mỗi lần in, nội dung $ntheo sau là chuỗi" $i "

  • evaldiễn giải các đối số của phần tiếp theo printftrước khi nó in kết quả của phần diễn giải đó dưới dạng các số bát phân được thêm vào bởi 2 dấu gạch chéo ngược một mảnh

  • cái cuối cùng printfin các giá trị byte cho các octals 2 đó tại một thời điểm theo sau là chuỗi -_ ---___cho mỗi cặp

  • $nđược khởi tạo thành một phương trình sẽ tăng thêm $imột cho mỗi đánh giá ngoại trừ việc nó bỏ qua các giá trị 10, 39 hoặc 47 - (tương ứng là \newline, 'trích dẫn đơn và /gạch chéo trong thập phân ASCII)

Kết quả cuối cùng là một thư mục chứa rất nhiều tên tệp thực sự xấu xí chứa mỗi byte trong bộ ký tự của tôi từ 1 đến 255 ngoại trừ trích dẫn đơn (chỉ bỏ qua để tránh thêm một sed s///câu lệnh)/dấu gạch chéo. Những tên tập tin trông như thế này:

(set -- *; printf '%s\n\n##############\n\n%s\n' "${9}" "${34}")  | cat -A

   ---___ww -_   ---___xx -_   ---___yy -_   ---___zz -_   ---___{{ -_   ---___|| -_   ---$
$
___}} -_   ---___~~ -_   ---___^?^? -_   ---___M-^@M-^@ -_   ---___M-^AM-^A -_   ---___M-^BM-^B -_   ---___M-^CM-^C$
$
##############$
$
 -_   ---___M-ZM-Z -_   ---___M-[M-[ -_   ---___M-\M-\ -_   ---___M-]M-] -_   ---___M-^M-^ -_   ---___M-_M-_ -_$
$
---___M-`M-` -_   ---___M-aM-a -_   ---___M-bM-b -_   ---___M-cM-c -_   ---___M-dM-d -_   ---___M-eM-e -_   ---___$

Bây giờ tôi sẽ nhận được một số dữ liệu trên các tệp này:

chksqz() ( LC_ALL=C sqz=$1
    set -- * ; set -f ; IFS= ; tc="$*"
    printf '#%s\n' \
        "There are $# files in this test directory." \
        "All filenames combined contain a total of ${#tc} bytes."
    IFS=$sqz ; set -- $* ; IFS= ; sc="$*"  
    printf "%s '$sqz'" \
        "#Of which ${#sc} bytes are not"\
        " and $((${#tc}-${#sc})) bytes are"
    set +f ; unset IFS
    printf ".\n#%s\n#Total:\t%d\n#Other:\t%d\n#'$sqz':\t%d\n" \
        "And to confirm these figures:" \
        $(  printf %s * | wc -c 
            printf %s * | tr -d "$sqz" | wc -c
            printf %s * | tr -dc "$sqz" | wc -c
))
chksqz '_ -'

ĐẦU RA

#There are 101 files in this test directory.
#All filenames combined contain a total of 17744 bytes.
#Of which 2692 bytes are not '_ -' and 15052 bytes are '_ -'.
#And to confirm these figures:
#Total: 17744
#Other: 2692
#'_ -': 15052

Đồng ý. Cuối cùng, hành động:

ifsqz '_ -'
chksqz '_ -'

ĐẦU RA

#There are 101 files in this test directory.
#All filenames combined contain a total of 2692 bytes.
#Of which 2692 bytes are not '_ -' and 0 bytes are '_ -'.
#And to confirm these figures:
#Total: 2692
#Other: 2692
#'_ -': 0

Sự thành công! Bạn có thể tự mình xem:

ls

????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
???????????????????????????
???????????????????????????
???????????????????????????
????????????????????????????
????????????????????????????
????????????????
??????????????????????
????????????????????????
??????????????????????????
??????????????????????????
??????????????????????????
??????????????????????????
???????????????????????????
???????????????????????????
???????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
??????????????????????????
????????????????????????
????????????????????
??????????????????
????????????????????????????
??
????????????????????????????
??????????????????????????
????????????????????????????
????????????????????????????
????????????????????!!""##
??????????????????!!""##$$
????????????????!!""##$$%%
????????????!!""##$$%%&&((
????????!!""??##$$%%&&(())
$$%%&&(())**??++,,..0011
%%&&(())**++??,,..00112233
&&(())**++,,??..0011223344
))**++,,..??0011223344556
**++,,..00??11223344556677
22334455667788??99::;;<<==>>
445566778899??::;;<<==>>??@@
5566778899::;;??<<==>>??@@AA
6778899::;;<<??==>>??@@AABB
8899::;;<<==??>>??@@AABBCCDD
\\]]^^``aa??bbccddeeffgghh
]]^^``aabbc??cddeeffgghhii
^^``aabbccdd??eeffgghhiijj
??@@AABBCCDDEE??FFGGHHIIJJKK
AABBCCDDEEFF??GGHHIIJJKKLLM
BBCCDDEEFFGG??HHIIJJKKLLMMNN
CCDDEEFFGGHHII??JJKKLLMMNNOO
EEFFGGHHIIJJ??KKLLMMNNOOPPQQ
ffgghhiijjkk??llmmnnooppqqrr
gghhiijjkkllmm??nnooppqqrrss
iijjkkllmmnn??ooppqqrrsstt
jjkkllmmnnoo??ppqqrrssttuuvv
kkllmmnnooppqq??rrssttuuvvww
LLMMNNOOPPQQRR??SSTTUUVVWWXX
MNNOOPPQQRRSS??TTUUVVWWXXYY
OOPPQQRRSSTT??UUVVWWXXYYZZ[[
PPQQRRSSTTUUVV??WWXXYYZZ[[\\
RRSSTTUUVVWW??XXYYZZ[[\\]]
ssttuuvvwwxx??yyzz{{||}}~~??
ttuuvvwwxxyyz??z{{||}}~~????
uuvvwwxxyyzz{{??||}}~~??????
wwxxyyzz{{||??}}~~??????????
xxyyzz{{||}}~~??????????????
YYZZ[[\\]]^^??``aabbccddee
ZZ[[\\]]^^``??aabbccddeeff

2
+1 cho việc sử dụng sáng tạo IFS+printf
John1024

@ John1024 - những gì thực sự thú vị:set -- 'some arbitrary' args; eval printf '"%s\n"' "$(IFS=0; printf ' "$@" %s' $(printf %025d))"
mikeerv

1
new="$(IFS=" -_"; printf %s $1)"tạo ra một subshell (ngoại trừ trong ksh93) và có vấn đề với các dòng mới. Một tùy chọn khác là sử dụng IFS=' -_'; set -- $1; IFS=; new="$*"(và thay đổi vòng lặp while của bạn thành vòng lặp for)
Stéphane Chazelas

1
[ -e x ]sẽ trả về false nếu xlà một liên kết tượng trưng đến một tệp không tồn tại hoặc không thể truy cập.
Stéphane Chazelas

1
Kung-Fu vỏ đẹp!
phản ứng

2

nếu bạn có perl, bạn thường đổi tên. bạn có thể làm:

> type rename
rename is /usr/bin/rename

và chỉ ra cách viết kịch bản này:

> cat /usr/bin/rename | head -n 5 #firt 5 lines for example
#!/usr/bin/perl -w
#
#  This script was developed by Robin Barker (Robin.Barker@npl.co.uk),
#  from Larry Wall's original script eg/rename from the perl source.
#

Kịch bản này không hỗ trợ cờ -i (đây là phiên bản trong hệ thống của tôi), nhưng có thể hỗ trợ của bạn. Còn tranh luận thì sao. Đầu tiên là các biểu thức thông thường với định dạng PCRE, nó hoạt động như bộ lọc, sửa đổi tên đầu vào thành tên đầu ra. Danh sách tên đầu vào bạn cung cấp bởi dấu hoa thị '*'. ví dụ: bạn làm:

> cd /tmp
> rename 's/ //g' *

trong thực '*' có thể được mở rộng thành:

> rename 's/ //g' file1 file2 file3 othe files found in current directory

Khi bạn có tập tin đếm thực sự lớn, bạn đang rơi vào bẫy. shell sẽ mở rộng dòng của bạn lâu hơn hệ thống chấp nhận. sau đó bạn có thể giải quyết bằng cách sử dụng find hoặc xargs. sử dụng 'find' là một vấn đề, bởi vì đổi tên sẽ được gọi nhiều lần bằng với số tập tin trong thư mục. sử dụng tốt hơn xargs với tùy chọn -r. một cuộc gọi đổi tên sửa đổi nhiều tập tin. ví dụ:

> ls | xargs -r rename 's/ //g'   #thats all, names will be appended at the end of this command.

vấn đề cuối cùng, nó có nghĩa là gì:

's/ //g'

đây là biểu thức chính quy để sửa đổi tên. sau đầu tiên '/' là không gian. điều này được phát hiện và được thay thế bằng chuỗi sau giây '/'. Nhưng có chuỗi trống kết thúc bằng '/' thứ ba, sau đó không gian được thay thế bằng không có gì. Tùy chọn 'g' làm cho biểu thức này lặp đi lặp lại. biểu thức sẽ đi bộ cho tất cả tên từ đầu đến cuối và phát hiện tất cả các khoảng trắng.

Nhưng nếu bạn có ký tự tab hoặc ký tự 'trắng' khác thì sao? có sự thay thế cho '\ s' này. những nhân vật không cần thiết khác? chỉ cần thêm nó vào biểu thức. Tất cả đóng với dấu ngoặc, ví dụ:

's/[\s_-]//g'

đây là tất cả. bạn có thấy sự tương đồng không? Tôi nghĩ bạn nên đọc man perlrequick và man perlretut, điều này giải thích cho bạn (tôi hy vọng) cách biểu hiện thường xuyên hoạt động. bạn có thể sử dụng lệnh đổi tên trong tập lệnh của riêng bạn nếu bạn cần.


1

shVòng lặp shell sau đây sẽ xóa tất cả các khoảng trắng, dấu gạch dưới và dấu gạch ngang khỏi tên của các tệp trong thư mục hiện tại, chú ý không ghi đè lên bất kỳ tệp hiện có nào:

for f in *; do
    test -f "$f" || continue
    nf=$( echo "$f" | tr -d ' _-' )
    ! test -e "$nf" && echo mv "$f" "$nf"
done

Cho bashksh, và dài dòng hơn một chút với logic:

for f in *; do
    if [[ -f "$f" ]]; then
        nf=$( tr -d ' _-' <<<"$f" )
        if [[ ! -e "$nf" ]]; then
            echo mv "$f" "$nf"
        fi
    fi
done

Xóa echokhi bạn chắc chắn nó sẽ làm những gì bạn muốn nó làm.

Các trlệnh sẽ xóa ( -d) bất kỳ ký tự trong tập hợp các ký tự ( ' _-'). Điều quan trọng là phải có dấu gạch ngang ở phần đầu hoặc phần cuối của tập hợp, hoặc nó sẽ được hiểu là một loạt các ký tự.

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.