Hapus spasi, tanda hubung, dan garis bawah pada nama file?

10

Apa perintah yang baik untuk menghapus spasi, tanda hubung, dan garis bawah dari semua file dalam direktori, atau file yang dipilih?

Saya menggunakan perintah berikut dengan Thunar Custom Actions untuk membasmi nama file:

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

Tapi perintah itu hanya menggantikan spasi dengan tanda hubung / tanda hubung dan huruf kecil yang dibatasi karakter.

Saya telah menggunakan perintah berikut di terminal untuk menghapus spasi dari ribuan nama file di folder, dan itu bekerja cukup cepat:

 rename "s/ //g" *

Sekali lagi, ini hanya menghapus spasi, dan bukan tanda hubung / garis bawah dan menggarisbawahi juga.

Idealnya saya tidak ingin spasi, tanda hubung / tanda hubung, dan garis bawah pada nama file saya. Dan alangkah baiknya jika perintah itu bisa digunakan dengan Thunar Custom Actions pada file yang dipilih.

pengguna8547
sumber
2
Saya perhatikan satu masalah yang dimiliki oleh banyak solusi yang diusulkan, adalah tidak benar memeriksa keberadaan nama "baru" sebelum membuat file. Tidak melakukan itu bisa menjadi sumber potensial dari banyak masalah.
mdpc
Apakah mungkin untuk memodifikasi perintah John1024 untuk memeriksa itu?
user8547
@ user8547rename -i "s/[-_ ]//g" *
Sparhawk
Terima kasih Sparhawk. Secara kebetulan, bagi mereka yang tertarik untuk menggunakan ini sebagai Tindakan Kustom Thunar, perintah untuk Thunar adalah: untuk file dalam% N; lakukan mv "$ file" echo $file | sed -e 's/[ _-]//g'; selesai
user8547

Jawaban:

11

Versi renameyang disertakan dengan perlpaket mendukung ekspresi reguler:

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

Kalau tidak,

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

The -ibendera akan membuat renamemenggunakan modus interaktif, mendorong jika target sudah ada, bukannya Timpa diam-diam.

Ganti nama Perl kadang-kadang disebut prename.

Ganti nama Perl versus ganti nama util-linux

Pada sistem seperti-Debian, nama perl perl tampaknya menjadi default dan perintah di atas hanya akan berfungsi.

Pada beberapa distribusi, renameutilitas dari util-linux adalah default. Utilitas ini sepenuhnya tidak kompatibel dengan Perl rename.

  • Semua: Pertama, periksa untuk melihat apakah Perl renametersedia di bawah nama prename.

  • Debian: Ganti nama Perl harus menjadi default. Ini juga tersedia sebagai prename. The renamedieksekusi, meskipun, adalah di bawah kendali /etc/alternativesdan dengan demikian bisa diubah untuk sesuatu yang berbeda.

  • archlinux: Jalankan pacman -S perl-renamedan perintah tersedia sebagai perl-rename. Untuk nama yang lebih nyaman, buat alias. (Kiat topi: ChiseledAbs)

  • Mac OSX Menurut jawaban ini , renamedapat diinstal pada OSX menggunakan homebrew via:

    brew install rename 
  • Unduh Langsung: rename juga tersedia dari Perl Monks:

     wget 'http://www.perlmonks.org/?displaytype=displaycode;node_id=303814' -O rename
John1024
sumber
Saya pikir itu tergantung pada apa renameyang Anda bicarakan. Yang dari util-linux -2.24.2-1.fc20.x86_64 tidak mendukung ekspresi reguler.
Cristian Ciupitu
1
@CristianCiupitu Saya baru saja memeriksa halaman manual untuk versi rename yang Anda temukan. Berdasarkan argumen, versi renameyang digunakan OP terlihat seperti perlversi dan bukan util-linuxversi.
John1024
Sebagai catatan, ini adalah renamehalaman manual untuk versi util-linux . Ngomong-ngomong, selain catatan itu, yang penting adalah OP mendapat jawabannya (dan Anda mendapat dukungan dari saya :-D).
Cristian Ciupitu
@CristianCiupitu Terima kasih telah menemukan itu. Kembali kepada Anda dengan +1.
John1024
1
@ John1024 archlinux, tetapi saya tahu caranya, langsung saja pacman -S perl-renamesaya kira Anda bisa alias.
ChiseledAbs
5

Saya akan mengganti semua trperintah itu, dengan sedperintah substitusi, misalnya:

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

Tidak menghitung mv, Anda tidak benar-benar membutuhkan proses luar untuk ini sama sekali - Anda dapat jenis hanya poof mereka.

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
)

Meski begitu, itu berarti mvdoa per file, dan jadi mungkin renamelebih baik. Meskipun ini harus bekerja diberikan hanya POSIX mvdalam $PATHdan POSIX shell.

Jadi, saya membuat semacam demo gila untuk ini. Set tes dihasilkan seperti:

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

Pertama-tama saya akan menjadi orang pertama yang mengakui bahwa perintah di atas menghasilkan hasil yang dapat lebih mudah diperoleh dengan cara lain. Tetapi cara lain kemungkinan tidak akan menunjukkan dengan baik apa yang mungkin dilakukan dengan $IFSsedikit imajinasi (sakit?) .

Jadi bit pertama cukup mudah:

  • tee mengeluarkan 5 salinan inputnya - heredocument dipanggil CGEN

  • dd blok inputnya oleh baris baru pada 90 byte per blok dan pipa itu untuk ...

  • sedbergabung dengan 2 dari blok-blok itu pada dua \nkarakter ewline, 'kutip-tunggal hasil, dan menambahkan string touch --untuk setiap siklus baris sebelum disalurkan ke ...

  • sh yang kemudian mengeksekusi semua input sebagai perintah shell

The #CGENbit meskipun ... Nah, sebentar ...

  • bagian bawah printfmencetak 252 0s

  • berikutnya dari terakhir menerima 252 ''argumen null-string dan untuk setiap cetakan isi $ndiikuti oleh string" $i "

  • evalmenafsirkan argumen berikutnya printfsebelum mencetak hasil penafsiran itu sebagai angka oktal yang ditopang oleh 2 backslash sepotong

  • yang terakhir printfmencetak nilai-nilai byte untuk oktal-2 itu pada suatu waktu diikuti oleh string -_ ---___untuk setiap pasangan

  • $ndiinisialisasi ke persamaan yang akan bertambah $isatu untuk setiap evaluasi kecuali bahwa ia melompati nilai 10, 39, atau 47 - (yang \newline, 'kutip tunggal, dan /garis miring dalam desimal ASCII secara berurutan )

Hasil akhirnya adalah direktori yang berisi banyak nama file yang benar-benar jelek yang mengandung setiap byte di charset saya dari 1 hingga 255 kecuali tanda kutip tunggal (hanya dilewati untuk menghindari satu sed s///pernyataan lagi ) dan /slash. Nama file tersebut terlihat seperti ini:

(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 -_   ---___$

Sekarang saya akan mendapatkan beberapa data pada file-file ini:

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 '_ -'

KELUARAN

#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

Baik. Akhirnya, untuk bertindak:

ifsqz '_ -'
chksqz '_ -'

KELUARAN

#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

Keberhasilan! Anda dapat melihat sendiri:

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
mikeserv
sumber
2
+1 untuk penggunaan kreatif IFS+printf
John1024
@ John1024 - apa yang benar-benar menyenangkan:set -- 'some arbitrary' args; eval printf '"%s\n"' "$(IFS=0; printf ' "$@" %s' $(printf %025d))"
mikeserv
1
new="$(IFS=" -_"; printf %s $1)"bercabang subkulit (kecuali dalam ksh93) dan memiliki masalah dengan tailing baris baru. Pilihan lain adalah menggunakan IFS=' -_'; set -- $1; IFS=; new="$*"(dan mengubah loop sementara Anda menjadi for for loop)
Stéphane Chazelas
1
[ -e x ]akan xmenghasilkan false jika symlink ke file yang tidak ada atau tidak dapat diakses.
Stéphane Chazelas
1
Cangkang bagus Kung-Fu!
countermode
2

jika Anda memiliki perl, Anda biasanya harus mengganti nama. Anda dapat melakukan:

> type rename
rename is /usr/bin/rename

dan tunjukkan bagaimana skrip ini ditulis:

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

Skrip ini tidak mendukung flag -i (ini adalah versi di sistem saya), tetapi mungkin skrip Anda mendukung. Bagaimana dengan argumen. Pertama adalah ekspresi reguler dengan format PCRE, berfungsi seperti filter, mengubah nama input menjadi nama output. Daftar nama input yang Anda berikan dengan tanda bintang '*'. misalnya, Anda melakukan:

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

'*' yang sebenarnya dapat diperluas ke:

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

Ketika Anda memiliki file jumlah sangat besar, Anda berada dalam perangkap. shell akan memperluas saluran Anda lebih lama dari yang diterima sistem. maka Anda dapat melakukan solusi menggunakan find atau xargs. menggunakan 'find' adalah masalah, karena mengganti nama akan dipanggil berkali-kali sama dengan jumlah file dalam direktori. lebih baik gunakan xargs dengan opsi -r. satu mengubah nama panggilan memodifikasi banyak file. sebagai contoh:

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

masalah terakhir, apa artinya:

's/ //g'

ini adalah ekspresi reguler untuk memodifikasi nama. setelah pertama '/' adalah ruang. ini terdeteksi, dan diganti dengan string setelah '/' kedua. Tetapi ada string kosong yang diakhiri dengan '/' ketiga, maka ruang diganti dengan tidak ada. Opsi 'g' membuat ungkapan ini berulang. ekspresi akan berjalan untuk semua nama dari awal hingga akhir, dan mendeteksi semua ruang.

Tetapi bagaimana jika Anda memiliki karakter tab atau karakter 'putih' lainnya? ada pengganti untuk ini 's'. karakter apa yang tidak dibutuhkan lainnya? cukup tambahkan ke ekspresi. Semua dekat dengan tanda kurung, misalnya:

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

ini semua apakah kamu melihat kesamaan? Saya pikir Anda harus membaca man perlrequick dan man perlretut, ini menjelaskan kepada Anda (saya harap) bagaimana ekspresi reguler bekerja. Anda dapat menggunakan perintah rename dalam skrip Anda sendiri jika Anda membutuhkannya.

Znik
sumber
1

shLingkaran shell berikut akan menghapus semua spasi, garis bawah, dan garis putus-putus dari nama-nama file di direktori saat ini, berhati-hati untuk tidak menimpa file yang ada:

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

Untuk bashdan ksh, dan sedikit lebih bertele-tele dengan logika:

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

Hapus echoketika Anda yakin itu melakukan apa yang Anda inginkan.

The trperintah akan menghapus ( -d) karakter apapun dalam himpunan karakter ( ' _-'). Sangat penting untuk memiliki tanda hubung di awal atau akhir himpunan, atau itu akan ditafsirkan sebagai serangkaian karakter.

Kusalananda
sumber