2010-01-22 8 views
6

Tengo que escribir un script bash que repetir el contenido de un directorio (incluyendo subdirectorios) y llevar a cabo las siguientes sustituciones:fiesta desafío scripting

  • reemplazar 'foo' en cualquier nombre de archivo con 'bar'
  • reemplazar 'foo' en el contenido de cualquier archivo con 'bar'

Hasta ahora todo lo que tengo es

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

:-)

+6

Debido a que mis padres no me muestran lo suficientemente afecto un niño –

+0

ahaha Le daré +1 solo por ese comentario. Ahora, ¿es esta tarea? –

+0

No, no es tarea. Si miras mi perfil es (con suerte) obvio que no soy un estudiante. –

Respuesta

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

Lamentablemente, el operador ',' no es estándar, y parece estar presente en GNU 'find'. Además, esto pasa directorios y archivos en 'sed', necesita agregar' -type f' para evitar errores. Me gusta porque es mucho más sucinto que la mayoría de las otras respuestas aquí. –

+0

Bueno, 'sed' arrojará un error en un directorio pero' find' continuará ;-) Sin ',' creo que esto debería dividirse en dos invocaciones 'find' o hacer que la parte Bash haga más trabajo. – ephemient

1

ACTUALIZADO: 1632 EST

  • Ahora se encarga de espacios en blanco, pero 'mientras lee elemento' nunca termina. Mejor, pero todavía no está bien. Mantendrá trabajando en esto.

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

'para el elemento en \' ... \ '' hace lo incorrecto si algún nombre de archivo contiene espacios en blanco. – ephemient

+0

@ephemient correcto, trabajando en esta solución ahora ... –

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

lenguaje muy agradable, travieso :) –

+0

@Don: Suena como un error de copiar y pegar. Intente escribir esto en su lugar. @dreynold: ten cuidado con los enlaces simbólicos, podrías quedarte atrapado en los bucles de directorio o ir a otro lugar ya que '..' podría no ser lo que crees que es. (Sí, Bash generalmente intenta arreglar esto, pero es una heurística. Consulte http://stackoverflow.com/questions/2105572) – ephemient

+0

@ephemient: Sí, tiene razón, esto no es lo más seguro. Corregido, creo. – dreynold

0

fiesta 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 

retire el 'eco' para confirmar los cambios

+0

Minit nit: si '$ line' es' '-e'' o '' -n'' u otro conmutador que Bash' echo' reconoce, se arruinará en el archivo. No hay nada de malo en realizar la sustitución incondicionalmente, así que cortaría ese todo mientras cuerpo a 'printf '% s \ n'" $ {línea // foo/barra} "'. Otra queja: ya estás usando la prueba de doble parche de Bash, ¿por qué no escribir el más corto '[[$ file = * foo *]]'? También tiene problemas difíciles de solucionar cuando cambia un nombre de directorio de nivel superior y está trabajando en algo más abajo en el árbol de directorios, y no cambia el nombre de los no directorios (lo que debería suceder, al leer la pregunta de OP). – ephemient

0

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

0

Sin embargo, otra solución hallazgo-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

En caso de que el nombre de archivo contenga "o \", debe usar algo más como '-exec bash -c 'path = $ 1; ...' - {} \;'. – ephemient

0

corrección de encontrar-ejecutivo de enfoque por gregb (comillas adición):

# 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'"'; 
... 
' \;