2010-01-22 17 views
6

ho bisogno di scrivere uno script bash che scorrere il contenuto di una directory (incluse le sottodirectory) ed effettuare le seguenti sostituzioni:bash scripting sfida

  • replace 'pippo' in qualsiasi nomi di file con 'bar'
  • sostituire 'pippo' nei contenuti di qualsiasi file con 'bar'

Finora tutto quello che ho è

find . -name '*' -exec {} \; 

:-)

+6

perché i miei genitori non mi hanno mostrato abbastanza affetto un bambino –

+0

ahaha Ti darò +1 solo per quel commento. Ora, sono questi compiti? –

+0

No, non è compito. Se guardi il mio profilo è (si spera) ovvio che non sono uno studente. –

risposta

2

Con RH rename:

find -f \(-exec sed -i s/foo/bar/g \; , -name \*foo\* -exec rename foo bar {} \; \) 
2
find "[email protected]" -depth -exec sed -i -e s/foo/bar/g {} \; , -name '*foo*' -print0 | 
while read -d '' file; do 
    base=$(basename "$file") 
    mv "$file" "$(dirname "$file")/${base//foo/bar}" 
done 
+0

Purtroppo l'operatore ',' non è standard, e sembra essere presente solo in GNU 'find'. Inoltre, questo passa directory e file in 'sed', è necessario aggiungere un' -tipo f' per evitare errori. Mi piace perché è molto più succinto di molte altre risposte qui. –

+0

Bene, 'sed' errore su una directory, ma' find' continuerà ;-) Senza ',' Penso che questo dovrebbe essere diviso in due invocazioni 'find' o fare in modo che la parte Bash faccia più lavoro. – ephemient

1

AGGIORNAMENTO: 1632 EST

  • Ora gestisce gli spazi ma 'mentre leggere voce' mai termina. Meglio, ma ancora non a destra. Continuerà a lavorare su .

aj @ mmdev0: ~/foo_to_bar $ cat script.sh

#!/bin/bash 

dirty=true 
while ${dirty} 
do 
    find ./ -name "*" |sed -s 's/ /\ /g'|while read item 
    do 
     if [[ ${item} == "./script.sh" ]] 
     then 
      continue 
     fi 
     echo "working on: ${item}" 

     if [[ ${item} == *foo* ]] 
     then 
      rename 's/foo/bar/' "${item}" 
      dirty=true 
      break 
     fi 

     if [[ ! -d ${item} ]] 
     then 
      cat "${item}" |sed -e 's/foo/bar/g' > "${item}".sed; mv "${item}".sed "${item}" 
     fi 
     dirty=false 
    done 
done 
+0

'per oggetto in \' ... \ '' fa la cosa sbagliata se qualsiasi nome di file contiene spazi vuoti. – ephemient

+0

@ephemient destra siete, lavorando su questa correzione ora ... –

1
#!/bin/bash 
function RecurseDirs 
{ 
oldIFS=$IFS 
IFS=$'\n' 
for f in * 
do 
    if [[ -f "${f}" ]]; then 
    newf=`echo "${f}" | sed -e 's/foo/bar/g'` 
    sed -e 's/foo/bar/g' < "${f}" > "${newf}" 
    fi 
    if [[ -d "${f}" && "${f}" != '.' && "${f}" != '..' && ! -L "${f}" ]]; then 
    cd "${f}" 
    RecurseDirs . 
    cd .. 
    fi 
done 
IFS=$oldIFS 
} 
RecurseDirs . 
+0

molto bello, la lingua godawful :) –

+0

@Don: Suona come un errore di copia e incolla. Prova a digitare questo invece. @dreynold: fai attenzione ai link simbolici, potresti rimanere bloccato nei loop delle directory o andare da qualche altra parte del tutto perché '..' potrebbe non essere quello che pensi sia. (Sì, Bash di solito cerca di risolvere il problema, ma è un euristico. Vedi http://stackoverflow.com/questions/2105572) – ephemient

+0

@ephemient: Sì, hai ragione - questa non è la cosa più sicura in circolazione. Corretto, credo. – dreynold

0

bash 4,0

#!/bin/bash 
shopt -s globstar 
path="/path" 
cd $path 
for file in ** 
do 
    if [ -d "$file" ] && [[ "$file" =~ ".*foo.*" ]];then 
     echo mv "$file" "${file//foo/bar}" 
    elif [ -f "$file" ];then 
     while read -r line 
     do 
      case "$line" in 
       *foo*) line="${line//foo/bar}";; 
      esac 
      echo "$line" 
     done < "$file" > temp 
     echo mv temp "$file" 

    fi 
done 

rimuovere il 'echo' a commettere modifiche

+0

Nit minore: se '$ line' è' '-e'' o '' -n'' o qualche altro interruttore che Bash' echo' riconosce, verrà manomesso nel file. Non c'è nulla di male nell'eseguire incondizionatamente la sostituzione, quindi taglierei il tutto mentre body down a 'printf '% s \ n'" $ {line // foo/bar} "'. Un altro pidocchio: stai già usando il test a doppia parentesi di Bash, perché non scrivere il più breve '[[$ file = * pippo *]]'? Hai anche problemi più difficili da correggere quando un dirname di livello superiore cambia e stai lavorando su qualcosa più in basso nell'albero delle directory, e non rinominare le non-directory (cosa che dovrebbe accadere leggendo la domanda dell'OP). – ephemient

0

for f in `tree -fi | grep foo`; do sed -i -e 's/foo/bar/g' $f ; done

0

Ancora un'altra soluzione find-exec:

find . -type f -exec bash -c ' 
path="{}"; 
dirName="${path%/*}"; 
baseName="${path##*/}"; 
nbaseName="${baseName/foo/bar}"; 
#nbaseName="${baseName//foo/bar}"; 
# cf. http://www.bash-hackers.org/wiki/doku.php?id=howto:edit-ed 
ed -s "${path}" <<< $'H\ng/foo/s/foo/bar/g\nwq'; 
#sed -i "" -e 's/foo/bar/g' "${path}"; # alternative for large files 
exec mv -iv "{}" "${dirName}/${nbaseName}" 
' \; 
+0

Nel caso in cui il nome del file contenga "o \, dovresti usare qualcosa di più come' -exec bash -c 'percorso = $ 1; ...' - {} \; '. – ephemient

0

correzione per trovare-exec approccio gregb (citazioni aggiunta):

# compare 

bash -c ' 
    echo $'a\nb\nc' 
' 

bash -c ' 
    echo $'"'a\nb\nc'"' 
' 

# therefore we need 

find . -type f -exec bash -c ' 
... 
ed -s "${path}" <<< $'"'H\ng/foo/s/foo/bar/g\nwq'"'; 
... 
' \; 
Problemi correlati