Tengo un problema con mi guión.
Preludio En primer lugar, tengo una lista, un archivo de 100 líneas como ese:
100;TEST ONE
101;TEST TWO
...
200;TEST HUNDRED
Cada línea tiene 2 argumentos. Por ejemplo, los argumentos de la primera línea son: "645", "PRUEBA UNO". Entonces el punto y coma es un delimitador.
Necesito poner ambos argumentos en dos variables. Digamos que será $id y $name. Para cada línea, los valores $id y $name serán diferentes. Por ejemplo, para la segunda línea $id = "646" y $name = "TEST TWO".
Después de eso, necesito tomar el archivo de muestra y cambiar las palabras clave predefinidas a los valores $id y $name. El archivo de muestra se ve así:
xxx is yyy
Y como resultado quiero tener 100 archivos con contenido diferente. Cada archivo debe contener datos $id y $name de cada línea. Y debe ser nombrado por su valor $name.
Ahí está mi guión:
#!/bin/bash -x
rm -f output/*
for i in $(cat list)
do
id="$(printf "$i" | awk -F ';' '{print $1}')"
name="$(printf "$i" | awk -F ';' '{print $2}')"
cp sample.xml output/input.tmp
sed -i -e "s/xxx/$id/g" output/input.tmp
sed -i -e "s/yyy/$name/g" output/input.tmp
mv output/input.tmp output/$name.xml
done
Entonces, solo trato de leer mi archivo de lista línea por línea. Para cada línea obtengo dos variables y luego las uso para reemplazar las palabras clave (xxx y yyy) del archivo de muestra y luego guardo el resultado.
Pero algo salió mal
Como resultado, solo tengo 1 archivo de salida. Y la depuración tiene mala pinta.
Aquí hay una ventana de depuración con solo 2 líneas en mi archivo de lista. Solo obtuve un archivo de salida. El nombre del archivo es simplemente "PRUEBA" y contiene una cadena: "101 es PRUEBA".
Se esperan dos archivos: "PRUEBA UNO", "PRUEBA DOS" y debe contener "100 es PRUEBA UNO" y "101 es PRUEBA DOS".
Como puede ver, la segunda variable tiene un espacio ("PRUEBA UNO", por ejemplo). Creo que el problema está relacionado con el símbolo especial del espacio, pero no sé por qué. Puse el parámetro -F awk en ";", por lo que awk debe interpretar solo el punto y coma como separador.
¿Que hice mal?
Respuesta1
Si te entiendo correctamente, puedes usar un bucle while y una expansión variable.
while IFS= read -r line; do
id="${line%;*}"
name="${line#*;}"
cp sample.xml output/input.tmp
sed -i -e "s/xxx/$id/g" output/input.tmp
sed -i -e "s/yyy/$name/g" output/input.tmp
mv output/input.tmp output/"$name".xml
done < file
Según lo propuesto por @steeldriver, aquí hay una opción (más elegante):
while IFS=';' read -r id name; do
cp sample.xml output/input.tmp
sed -i -e "s/xxx/$id/g" output/input.tmp
sed -i -e "s/yyy/$name/g" output/input.tmp
mv output/input.tmp output/"$name".xml
done < file
Respuesta2
Citando!!. Falta la cita en esta línea:
mv output/input.tmp output/$name.xml
Debería ser:
mv output/input.tmp output/"$name".xml
para evitar problemas con un nombre de archivo con espacios.
Y la expansión de $(cat list)
está siendo dividida (y globalizada) por el caparazón, que también se rompe en espacios.
Quizás puedas cambiar a este script:
#!/bin/bash -x
rm -f output/*
inputfile=output/input.tmp
while read -r line
do
id=${line%%;*}
name=${line##*;}
cp sample.xml "$inputfile"
sed -i -e "s/xxx/$id/g" "$inputfile"
sed -i -e "s/yyy/$name/g" "$inputfile"
mv "$inputfile" output/"$name".xml; echo
done <list
Respuesta3
La razón por la que su awk no produce los resultados esperados se debe a la forma en que está iterando sobre el archivo. Cuando iteras usando for i in $(cat file)
, estás iterando sobre palabras (divididas por IFS), no sobre líneas. Para leer un archivo línea por línea, use while read
:
while read -r line; do
...
done < file
Para obtener más información, consulte las siguientes preguntas frecuentes sobre bash:¿Cómo puedo leer un archivo (flujo de datos, variable) línea por línea (y/o campo por campo)?
Respuesta4
Como enfoque alternativo,puedes hacer este trabajo con awken 1 proceso en lugar de 4 para cada línea. Es más probable que esto sea beneficioso si hay muchas líneas en la lista pero sample.xml es pequeño.
awk -F';' 'FNR==NR{x=x $0 RS; next}
{t=x; gsub(/xxx/,$1,t); gsub(/yyy/,$2,t); f="output/"$2".xml"; printf "%s",t >f; close(f)}
' sample.xml list
# shown with unnecessary linebreaks for clarity, but you can put it all on one line
Si la lista tiene finales de línea CRLF (también conocido como formato DOS o Windows) como se comentó en su Q, y no puede (fácilmente) o no quiere eliminarlos primero, awk también puede manejar eso; justo después de la segunda {
inserción sub(/\r$/,"",$0);
(o $2
si lo prefiere).
perl también puede hacer esto (perl puede hacer casi todo lo que awk puede hacer), pero de manera un poco más detallada, y aunque perl está comúnmente disponible, no es POSIX como lo es awk.