Eliminare spazi, trattini e caratteri di sottolineatura nei nomi dei file?


10

Qual è un buon comando per eliminare spazi, trattini e caratteri di sottolineatura da tutti i file in una directory o file selezionati?

Uso il seguente comando con le azioni personalizzate Thunar per eseguire il slugify dei nomi di file:

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

Ma quel comando sostituisce solo gli spazi con trattini / trattini e caratteri minuscoli.

Ho usato il seguente comando nel terminale per eliminare spazi da migliaia di nomi di file in una cartella e ha funzionato abbastanza velocemente:

 rename "s/ //g" *

Ancora una volta, elimina solo gli spazi e non anche trattini / trattini e caratteri di sottolineatura.

Idealmente, non voglio spazi, trattini / trattini e caratteri di sottolineatura nei nomi dei file. E sarebbe fantastico se il comando potesse essere utilizzato con le azioni personalizzate Thunar sui file selezionati.


2
Prendo atto di un problema che molte delle soluzioni proposte presentano, non sta controllando correttamente l'esistenza del "nuovo" nome prima di creare il file. Non farlo potrebbe essere la potenziale fonte di molti problemi.
mdpc,

È possibile modificare il comando di John1024 per verificarlo?
user8547

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

Grazie Sparhawk. Per inciso, per coloro che sono interessati a usarlo come azione personalizzata Thunar, il comando per Thunar è: per il file in% N; fare mv "$ file" echo $file | sed -e 's/[ _-]//g'; fatto
user8547

Risposte:


11

La versione di renamequesto viene fornita con il perlpacchetto supporta espressioni regolari:

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

In alternativa,

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

Il -iflag renameutilizzerà la modalità interattiva, chiedendo se l'obiettivo esiste già, invece di sovrascriverlo silenziosamente.

La ridenominazione di Perl viene talvolta chiamata prename.

Rinomina di Perl rispetto a quella di util-linux

Su sistemi simili a Debian, la ridenominazione di perl sembra essere l'impostazione predefinita e i comandi sopra dovrebbero funzionare.

Su alcune distribuzioni, l' renameutility di util-linux è l'impostazione predefinita. Questa utility è completamente incompatibile con Perl rename.

  • Tutto: per prima cosa controlla se Perl renameè disponibile sotto il nome prename.

  • Debian: il nome di Perl dovrebbe essere il default. È anche disponibile come prename. L' renameeseguibile, tuttavia, è sotto il controllo di /etc/alternativese quindi potrebbe essere stato modificato in qualcosa di diverso.

  • archlinux: esegui pacman -S perl-renamee il comando è disponibile come perl-rename. Per un nome più conveniente, creare un alias. (Punta del cappello: ChiseledAbs)

  • Mac OSX Secondo questa risposta , renamepuò essere installato su OSX usando homebrew tramite:

    brew install rename 
  • Download diretto: rename è disponibile anche da Perl Monks:

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

Penso che dipenda da cosa renamestai parlando. Quello di util-linux -2.24.2-1.fc20.x86_64 non supporta le espressioni regolari.
Cristian Ciupitu,

1
@CristianCiupitu Ho appena controllato la pagina man per la versione di rinomina che hai trovato. Sulla base degli argomenti, la versione renameutilizzata dall'OP assomiglia alla perlversione e non alla util-linuxversione.
Giovanni 1024,

Per la cronaca, questa è la renamepagina man della versione util-linux . Comunque, oltre a quella nota, l'importante è che l'OP abbia ottenuto la sua risposta (e tu un voto da parte mia MrGreen).
Cristian Ciupitu,

@CristianCiupitu Grazie per averlo trovato. Di nuovo con un +1.
Giovanni 1024,

1
@ John1024 archlinux, ma ho scoperto come, basta andare pacman -S perl-renamequindi credo che tu possa alias.
CesellatoAbs

5

Sostituirei tutti quei trcomandi, con un sedcomando di sostituzione, ad esempio:

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

4

Non contando mv, non hai davvero bisogno di un processo esterno per questo - puoi in qualche modo limitarti .

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
)

Tuttavia, ciò significa una mvchiamata per file, e quindi probabilmente renameè meglio. Anche se questo dovrebbe funzionare dato solo un POSIX mvin $PATHe una shell POSIX.

Quindi, ho pensato a una specie di demo folle per questo. Il set di test viene generato come:

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

In primo luogo, sarò il primo a riconoscere che il comando sopra riportato produce risultati che possono essere ottenuti più facilmente con altri mezzi. Ma altri mezzi probabilmente non dimostrerebbero anche cosa si potrebbe fare $IFSe un po ' (malato?) Immaginazione.

Quindi il primo bit è abbastanza semplice:

  • tee distribuisce 5 copie del suo input - il documento ereditario chiamato CGEN

  • dd blocca il suo input da newline a 90 byte per blocco e pipe che per ...

  • sedunisce 2 di quei blocchi su due \ncaratteri di ewline, 'virgolette singole i risultati e antepone la stringa touch --per ogni ciclo di linea prima di eseguire il piping su ...

  • sh che quindi esegue tutti gli input come comandi di shell

Un #CGENpo 'però ... Beh, brevemente ...

  • la parte inferiore printfstampa 252 0s

  • il successivo dall'ultimo riceve 252 ''argomenti a stringa nulla e per ciascuno stampa il contenuto di $nseguito dalla stringa" $i "

  • evalinterpreta gli argomenti del prossimo up printfprima di stampare i risultati di tale interpretazione come numeri ottali anteposti da 2 barre rovesciate di un pezzo

  • l'ultimo printfstampa i valori dei byte per quegli ottali 2 alla volta seguiti dalla stringa -_ ---___per ciascuna coppia

  • $nviene inizializzato su un'equazione che aumenterà $idi uno per ogni valutazione, tranne per il fatto che salta i valori 10, 39 o 47 - (che sono rispettivamente \newline, virgoletta 'singola e /barra in decimale ASCII)

Il risultato finale è una directory che contiene molti nomi di file davvero brutti che contengono tutti i byte nel mio set di caratteri da 1 a 255 tranne la virgoletta singola (saltata solo per evitare un'altra sed s///istruzione) e la /barra. Quei nomi di file sembrano così:

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

Ora avrò alcuni dati su questi file:

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

PRODUZIONE

#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

Ok. Ora finalmente, all'azione:

ifsqz '_ -'
chksqz '_ -'

PRODUZIONE

#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

Successo! Puoi vederlo da solo:

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 per un uso creativo di IFS+printf
Giovanni 1024

@ John1024 - cosa è davvero divertente:set -- 'some arbitrary' args; eval printf '"%s\n"' "$(IFS=0; printf ' "$@" %s' $(printf %025d))"
mikeserv

1
new="$(IFS=" -_"; printf %s $1)"crea una subshell (eccetto in ksh93) e ha problemi con la coda delle code. Un'altra opzione è quella di usare IFS=' -_'; set -- $1; IFS=; new="$*"(e cambiare il tuo ciclo while in un ciclo for)
Stéphane Chazelas,

1
[ -e x ]restituirà false se xè un collegamento simbolico a un file inesistente o non accessibile.
Stéphane Chazelas,

1
Bella conchiglia Kung-Fu!
Contromodalità

2

se hai il perl, di solito hai un nuovo nome. tu puoi fare:

> type rename
rename is /usr/bin/rename

e mostra come è scritto questo script:

> 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.
#

Questo script non supporta -i flag (questa è la versione nel mio sistema), ma forse la tua supporta. Che dire degli argomenti. Il primo è le espressioni regolari con il formato PCRE, funziona come filtro, modifica il nome di input in nome di output. Elenco dei nomi di input forniti dall'asterisco '*'. ad esempio, fai:

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

in realtà '*' può essere espanso in:

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

Quando hai file di conteggio davvero grandi, sei in trappola. shell espanderà la tua linea più a lungo di quanto il sistema accetti. quindi puoi risolvere il problema usando find o xargs. l'utilizzo di 'trova' è un problema, perché la ridenominazione verrà chiamata molte volte uguale al conteggio dei file nella directory. meglio usare xargs con l'opzione -r. una chiamata di rinomina modifica molti file. per esempio:

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

ultimo problema, cosa significa:

's/ //g'

questa è un'espressione regolare per i nomi di modifica. dopo il primo '/' è lo spazio. questo viene rilevato e sostituito dalla stringa dopo il secondo '/'. Ma c'è una stringa vuota terminata con il terzo '/', quindi lo spazio non viene sostituito da nulla. L'opzione 'g' rende questa espressione ripetitiva. espressione camminerà per tutti i nomi dall'inizio alla fine e rileverà tutti gli spazi.

E se hai un carattere tab o un altro carattere "bianco"? c'è una sostituzione per questo '\ s'. quali altri personaggi non necessari? aggiungilo semplicemente all'espressione. Tutti si chiudono con parentesi, ad esempio:

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

questo è tutto. vedi somiglianza? Penso che dovresti leggere man perlrequick e man perlretut, questo ti spiega (spero) come funziona l'espressione regolare. puoi usare il comando rename nel tuo script se ne hai bisogno.


1

Il seguente shloop di shell rimuoverà tutti gli spazi, i trattini bassi e i trattini dai nomi dei file nella directory corrente, facendo attenzione a non sovrascrivere i file esistenti:

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

Per bashe ksh, ed essendo leggermente più prolisso con la logica:

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

Rimuovi echoquando sei sicuro che fa quello che vuoi che faccia.

Il trcomando eliminerà ( -d) qualsiasi carattere nel set di caratteri specificato ( ' _-'). È importante avere il trattino all'inizio o alla fine del set, altrimenti verrà interpretato come un intervallo di caratteri.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.