una ultima sustituir reemplazar linea insertar fichero especiales eliminar comando caracteres caracter cadena linux perl shell awk sed

linux - ultima - ¿Cómo puedo eliminar una nueva línea si es el último carácter en un archivo?



sed reemplazar/ (21)

Aquí hay una solución bonita y ordenada de Python. No intenté ser terco aquí.

Esto modifica el archivo in situ, en lugar de hacer una copia del archivo y quitar la nueva línea de la última línea de la copia. Si el archivo es grande, será mucho más rápido que la solución Perl que se eligió como la mejor respuesta.

Trunca un archivo en dos bytes si los dos últimos bytes son CR / LF, o en un byte si el último byte es LF. No intenta modificar el archivo si el último byte (s) no es (CR) LF. Maneja errores. Probado en Python 2.6.

Pon esto en un archivo llamado "striplast" y chmod +x striplast .

#!/usr/bin/python # strip newline from last line of a file import sys def trunc(filename, new_len): try: # open with mode "append" so we have permission to modify # cannot open with mode "write" because that clobbers the file! f = open(filename, "ab") f.truncate(new_len) f.close() except IOError: print "cannot write to file:", filename sys.exit(2) # get input argument if len(sys.argv) == 2: filename = sys.argv[1] else: filename = "--help" # wrong number of arguments so print help if filename == "--help" or filename == "-h" or filename == "/?": print "Usage: %s <filename>" % sys.argv[0] print "Strips a newline off the last line of a file." sys.exit(1) try: # must have mode "b" (binary) to allow f.seek() with negative offset f = open(filename, "rb") except IOError: print "file does not exist:", filename sys.exit(2) SEEK_EOF = 2 f.seek(-2, SEEK_EOF) # seek to two bytes before end of file end_pos = f.tell() line = f.read() f.close() if line.endswith("/r/n"): trunc(filename, end_pos) elif line.endswith("/n"): trunc(filename, end_pos + 1)

PD En el espíritu de "Perl golf", esta es mi solución Python más corta. Absorbe todo el archivo de la entrada estándar en la memoria, quita todas las nuevas líneas del final y escribe el resultado en salida estándar. No es tan escueto como el Perl; simplemente no puedes vencer a Perl por cosas complicadas como esta.

Elimine "/ n" de la llamada a .rstrip() y eliminará todos los espacios en blanco del final del archivo, incluidas varias líneas en blanco.

Pon esto en "slurp_and_chomp.py" y luego ejecuta python slurp_and_chomp.py < inputfile > outputfile .

import sys sys.stdout.write(sys.stdin.read().rstrip("/n"))

Tengo algunos archivos que me gustaría eliminar la última línea nueva si es el último carácter en un archivo. od -c me muestra que el comando que ejecuto escribe el archivo con una nueva línea final:

0013600 n t > /n

He intentado algunos trucos con sed, pero lo mejor que pude pensar es que no está haciendo el truco:

sed -e ''$s//(.*/)/n$//1/'' abc

¿Alguna idea de como hacer esto?


Asumiendo el tipo de archivo Unix y solo quieres la última línea nueva, esto funciona.

sed -e ''${/^$/d}''

No funcionará en múltiples líneas nuevas ...

* Funciona solo si la última línea es una línea en blanco.


La única vez que he querido hacer esto es para code golf, y luego acabo de copiar mi código fuera del archivo y pegarlo en un echo -n ''content''>file declaración de echo -n ''content''>file .


POSIX SED:

''${/^$/d}''

$ - match last line { COMMANDS } - A group of commands may be enclosed between { and } characters. This is particularly useful when you want a group of commands to be triggered by a single address (or address-range) match.


Puede aprovechar el hecho de que las sustituciones de comandos de shell eliminan los caracteres de nueva línea rezagados :

Forma simple que funciona en bash, ksh, zsh:

printf %s "$(< in.txt)" > out.txt

Alternativa portátil (compatible con POSIX) (ligeramente menos eficiente):

printf %s "$(cat in.txt)" > out.txt

Nota:

  • Si in.txt termina con múltiples caracteres de nueva línea, la sustitución del comando los elimina a todos . Gracias, @Sparhawk. (No elimina caracteres en espacios en blanco que no sean líneas de seguimiento recientes).
  • Como este enfoque lee todo el archivo de entrada en la memoria , solo es recomendable para archivos más pequeños.
  • printf %s asegura que no se agrega una nueva línea a la salida (es la alternativa que cumple con POSIX al echo -n no estándar echo -n ; vea http://pubs.opengroup.org/onlinepubs/009696799/utilities/echo.html y https://unix.stackexchange.com/a/65819 )

Una guía para las otras respuestas :

  • Si Perl está disponible, elija la respuesta aceptada : es simple y eficiente en la memoria (no lee todo el archivo de entrada a la vez).

  • De lo contrario, considere la respuesta Awk de ghostdog74 : es oscura, pero también eficiente en la memoria ; un equivalente más legible (compatible con POSIX) es:

    • awk ''NR > 1 { print prev } { prev=$0 } END { ORS=""; print }'' in.txt
    • La impresión se retrasa en una línea, de modo que la línea final se puede manejar en el bloque END , donde se imprime sin un /n nque arrastra debido a la configuración del separador de registros de salida ( OFS ) en una cadena vacía.
  • Si desea una solución detallada, pero rápida y sólida que verdaderamente se edite in situ (en lugar de crear un archivo temporal que luego reemplaza el original), considere la secuencia de comandos Perl de jrockway .


Puedes hacer esto con head desde GNU coreutils, soporta argumentos que son relativos al final del archivo. Así que para dejar el último byte de uso:

head -c -1

Para probar una nueva línea final, puede usar tail y wc . El siguiente ejemplo guarda el resultado en un archivo temporal y posteriormente sobrescribe el original:

if [[ $(tail -c1 file | wc -l) == 1 ]]; then head -c -1 file > file.tmp mv file.tmp file fi

También puede usar la sponge de moreutils para realizar la edición "en el lugar":

[[ $(tail -c1 file | wc -l) == 1 ]] && head -c -1 file | sponge file

También puede hacer una función general reutilizable rellenando esto en su archivo .bashrc :

# Example: remove-last-newline < multiline.txt function remove-last-newline(){ local file=$(mktemp) cat > $file if [[ $(tail -c1 $file | wc -l) == 1 ]]; then head -c -1 $file > $file.tmp mv $file.tmp $file fi cat $file }

Actualizar

Como notó KarlWilbur en los comentarios y se usó en la respuesta de Sorentar , truncate --size=-1 puede reemplazar el head -c-1 y admite la edición in situ.


Si quieres hacerlo bien, necesitas algo como esto:

use autodie qw(open sysseek sysread truncate); my $file = shift; open my $fh, ''+>>'', $file; my $pos = tell $fh; sysseek $fh, $pos - 1, 0; sysread $fh, my $buf, 1 or die ''No data to read?''; if($buf eq "/n"){ truncate $fh, $pos - 1; }

Abrimos el archivo para leerlo y anexarlo; abrir para agregar significa que ya hemos seek hasta el final del archivo. Luego obtenemos la posición numérica del final del archivo con tell . Usamos ese número para buscar un personaje, y luego leemos ese personaje. Si se trata de una nueva línea, truncamos el archivo al personaje antes de esa nueva línea, de lo contrario, no hacemos nada.

Esto se ejecuta en tiempo constante y espacio constante para cualquier entrada, y tampoco requiere más espacio en el disco.


Sin embargo, otra respuesta FTR (¡y mi favorita!): Echo / cat, la cosa que quieres quitar y capturar la salida a través de los apoyos. La nueva línea final será eliminada. Por ejemplo:

# Sadly, outputs newline, and we have to feed the newline to sed to be portable echo thingy | sed -e ''s/thing/sill/'' # No newline! Happy. out=`echo thingy | sed -e ''s/thing/sill/''` printf %s "$out" # Similarly for files: file=`cat file_ending_in_newline` printf %s "$file" > file_no_newline


Sin embargo, otro WTDI perl:

perl -i -p0777we''s//n/z//'' filename


Tenía un problema similar, pero estaba trabajando con un archivo de Windows y necesito mantener esos CRLF, mi solución en Linux:

sed ''s//r//g'' orig | awk ''{if (NR>1) printf("/r/n"); printf("%s",$0)}'' > tweaked


Un método muy simple para archivos de una línea, que requiere el eco de GNU de coreutils:

/bin/echo -n $(cat $file)


Una solución rápida es usar la utilidad gnu truncate:

[ -z $(tail -c1 file) ] && truncate -s-1

La prueba será verdadera si el archivo tiene una nueva línea final.

La eliminación es muy rápida, realmente está en su lugar, no se necesita ningún archivo nuevo y la búsqueda también está leyendo al final solo un byte (cola -c1).


Usando dd:

file=''/path/to/file'' [[ "$(tail -c 1 "${file}" | tr -dc ''/n'' | wc -c)" -eq 1 ]] && / printf "" | dd of="${file}" seek=$(($(stat -f "%z" "${file}") - 1)) bs=1 count=1 #printf "" | dd of="${file}" seek=$(($(wc -c < "${file}") - 1)) bs=1 count=1


papar moscas

awk ''{q=p;p=$0}NR>1{print q}END{ORS = ""; print p}'' file


rubí:

ruby -ne ''print $stdin.eof ? $_.strip : $_''

o:

ruby -ane ''q=p;p=$_;puts q if $.>1;END{print p.strip!}''



head -n -1 abc > newfile tail -n 1 abc | tr -d ''/n'' >> newfile

Editar 2:

Aquí hay una versión de awk (corregida) que no acumula una matriz potencialmente enorme:

awk ''{if (línea) línea de impresión; line = $ 0} END {printf $ 0} ''abc


perl -pe ''chomp if eof'' filename >filename2

o, para editar el archivo en su lugar:

perl -pi -e ''chomp if eof'' filename

[Nota del editor: -pi -e fue originalmente -pie , pero, como lo mencionaron varios comentaristas y explicado por @hvd, este último no funciona.]

Esto fue descrito como una "blasfemia perl" en el sitio web awk que vi.

Pero, en una prueba, funcionó.


perl -pi -e ''s//n$// if(eof)'' your_file


sed '':a;/^/n*$/{$d;N;};//n$/ba'' file


sed -n "1 x;1 !H $ {x;s//n*$//p;} " YourFile

Debería eliminar cualquier última ocurrencia de / n en el archivo. No funciona en archivos grandes (debido a la limitación del buffer sed)