Dado que el objetivo principal de este hilo es cómo hacer SAVE in situ en NON GNU awk
así que estoy publicando primero su plantilla que ayudará a cualquier persona en cualquier tipo de requisito, necesitan agregar/agregar BEGIN
y END
sección en su código manteniendo su BLOQUE principal según su requisito y debe hacer la edición en el lugar entonces:
NOTA: A continuación, se escribirá toda su salida en el archivo de salida, por lo que, en caso de que desee imprimir algo en la salida estándar, solo agregue print...
sentencia sin > (out)
a continuación.
Plantilla genérica:
awk -v out_file="out" '
FNR==1{
close(out)
out=out_file count++
rename=(rename?rename ORS:"") "mv \047" out "\047 \047" FILENAME "\047"
}
{
.....your main block code.....
}
END{
if(rename){
system(rename)
}
}
' *.txt
Solución de muestra específica proporcionada:
Se me ocurrió el siguiente enfoque dentro de awk
en sí mismo (para muestras adicionales, el siguiente es mi enfoque para resolver esto y guardar la salida en Input_file)
awk -v out_file="out" '
FNR==1{
close(out)
out=out_file count++
rename=(rename?rename ORS:"") "mv \047" out "\047 \047" FILENAME "\047"
}
{
print FNR > (out)
}
END{
if(rename){
system(rename)
}
}
' *.txt
NOTA:esto es solo una prueba para guardar la salida editada en Input_file(s), uno podría usar su sección BEGIN, junto con su sección END en su programa, la sección principal debe ser según el requisito de específico pregunta en sí.
Advertencia justa: Además, dado que este enfoque crea un nuevo archivo de salida temporal en la ruta, es mejor asegurarse de que tenemos suficiente espacio en los sistemas, aunque en el resultado final esto mantendrá solo los archivos de entrada principales, pero durante las operaciones necesita espacio en el sistema/directorio
A continuación se muestra una prueba para el código anterior.
Ejecución del programa con un ejemplo: Supongamos que los siguientes son los .txt
Archivo(s) de entrada:
cat << EOF > test1.txt
onetwo three
tets testtest
EOF
cat << EOF > test2.txt
onetwo three
tets testtest
EOF
cat << EOF > test3.txt
onetwo three
tets testtest
EOF
Ahora, cuando ejecutamos el siguiente código:
awk -v out_file="out" '
FNR==1{
close(out)
out=out_file count++
rename=(rename?rename ORS:"") "mv \047" out "\047 \047" FILENAME "\047"
}
{
print "new_lines_here...." > (out)
}
END{
if(rename){
system("ls -lhtr;" rename)
}
}
' *.txt
NOTA: Tengo lugar ls -lhtr
en system
sección intencionalmente para ver qué archivos de salida está creando (de forma temporal) porque luego los renombrará a su nombre real.
-rw-r--r-- 1 runner runner 27 Dec 9 05:33 test2.txt
-rw-r--r-- 1 runner runner 27 Dec 9 05:33 test1.txt
-rw-r--r-- 1 runner runner 27 Dec 9 05:33 test3.txt
-rw-r--r-- 1 runner runner 38 Dec 9 05:33 out2
-rw-r--r-- 1 runner runner 38 Dec 9 05:33 out1
-rw-r--r-- 1 runner runner 38 Dec 9 05:33 out0
Cuando hacemos un ls -lhtr
después de awk
la secuencia de comandos se realiza con la ejecución, solo pudimos ver .txt
archivos allí.
-rw-r--r-- 1 runner runner 27 Dec 9 05:33 test2.txt
-rw-r--r-- 1 runner runner 27 Dec 9 05:33 test1.txt
-rw-r--r-- 1 runner runner 27 Dec 9 05:33 test3.txt
Explicación: Agregando una explicación detallada del comando anterior aquí:
awk -v out_file="out" ' ##Starting awk program from here, creating a variable named out_file whose value SHOULD BE a name of files which are NOT present in our current directory. Basically by this name temporary files will be created which will be later renamed to actual files.
FNR==1{ ##Checking condition if this is very first line of current Input_file then do following.
close(out) ##Using close function of awk here, because we are putting output to temp files and then renaming them so making sure that we shouldn't get too many files opened error by CLOSING it.
out=out_file count++ ##Creating out variable here, whose value is value of variable out_file(defined in awk -v section) then variable count whose value will be keep increment with 1 whenever cursor comes here.
rename=(rename?rename ORS:"") "mv \047" out "\047 \047" FILENAME "\047" ##Creating a variable named rename, whose work is to execute commands(rename ones) once we are done with processing all the Input_file(s), this will be executed in END section.
} ##Closing BLOCK for FNR==1 condition here.
{ ##Starting main BLOCK from here.
print "new_lines_here...." > (out) ##Doing printing in this example to out file.
} ##Closing main BLOCK here.
END{ ##Starting END block for this specific program here.
if(rename){ ##Checking condition if rename variable is NOT NULL then do following.
system(rename) ##Using system command and placing renme variable inside which will actually execute mv commands to rename files from out01 etc to Input_file etc.
}
} ##Closing END block of this program here.
' *.txt ##Mentioning Input_file(s) with their extensions here.
Probablemente elegiría algo como esto si intentara hacer esto:
$ cat ../tst.awk
FNR==1 { saveChanges() }
{ print FNR > new }
END { saveChanges() }
function saveChanges( bak, result, mkBackup, overwriteOrig, rmBackup) {
if ( new != "" ) {
bak = old ".bak"
mkBackup = "cp \047" old "\047 \047" bak "\047; echo \"$?\""
if ( (mkBackup | getline result) > 0 ) {
if (result == 0) {
overwriteOrig = "mv \047" new "\047 \047" old "\047; echo \"$?\""
if ( (overwriteOrig | getline result) > 0 ) {
if (result == 0) {
rmBackup = "rm -f \047" bak "\047"
system(rmBackup)
}
}
}
}
close(rmBackup)
close(overwriteOrig)
close(mkBackup)
}
old = FILENAME
new = FILENAME ".new"
}
$ awk -f ../tst.awk test1.txt test2.txt test3.txt
Hubiera preferido copiar primero el archivo original en la copia de seguridad y luego operar sobre los cambios guardados en el original, pero hacerlo cambiaría el valor de la variable NOMBRE DE ARCHIVO para cada archivo de entrada que no es deseable.
Tenga en cuenta que si tuviera un archivo original llamado whatever.bak
o whatever.new
en su directorio, los sobrescribirá con archivos temporales, por lo que también deberá agregar una prueba para eso. Una llamada al mktemp
obtener los nombres de los archivos temporales sería más sólido.
Lo MUCHO más útil en esta situación sería una herramienta que ejecute cualquier otro comando y haga la parte de edición "en el lugar", ya que podría usarse para proporcionar edición "en el lugar" para POSIX sed, awk, grep, tr, lo que sea y no requeriría que cambie la sintaxis de su secuencia de comandos a print > out
etc. cada vez que desee imprimir un valor. Un ejemplo simple y frágil:
$ cat inedit
#!/bin/env bash
for (( pos=$#; pos>1; pos-- )); do
if [[ -f "${!pos}" ]]; then
filesStartPos="$pos"
else
break
fi
done
files=()
cmd=()
for (( pos=1; pos<=$#; pos++)); do
arg="${!pos}"
if (( pos < filesStartPos )); then
cmd+=( "$arg" )
else
files+=( "$arg" )
fi
done
tmp=$(mktemp)
trap 'rm -f "$tmp"; exit' 0
for file in "${files[@]}"; do
"${cmd[@]}" "$file" > "$tmp" && mv -- "$tmp" "$file"
done
que usaría de la siguiente manera:
$ awk '{print FNR}' test1.txt test2.txt test3.txt
1
2
1
2
1
2
$ ./inedit awk '{print FNR}' test1.txt test2.txt test3.txt
$ tail test1.txt test2.txt test3.txt
==> test1.txt <==
1
2
==> test2.txt <==
1
2
==> test3.txt <==
1
2
Un problema obvio con ese inedit
script es la dificultad de identificar los archivos de entrada/salida por separado del comando cuando tiene varios archivos de entrada. La secuencia de comandos anterior asume que todos los archivos de entrada aparecen como una lista al final del comando y el comando se ejecuta contra ellos uno a la vez, pero por supuesto eso significa que no puede usarlo para secuencias de comandos que requieren 2 o más archivos a la vez. un tiempo, por ejemplo:
awk 'NR==FNR{a[$1];next} $1 in a' file1 file2
o scripts que establecen variables entre archivos en la lista de argumentos, por ejemplo:
awk '{print $7}' FS=',' file1 FS=':' file2
Hacerlo más sólido se deja como ejercicio para el lector, pero observe el xargs
sinopsis como punto de partida de cómo un sólido inedit
tendría que funcionar :-).