¿Cómo elimino el sufijo del archivo y la parte de la ruta de una cadena de ruta en Bash?

StackOverflow https://stackoverflow.com/questions/125281

  •  02-07-2019
  •  | 
  •  

Pregunta

Dada una ruta de archivo de cadena como /foo/fizzbuzz.bar, ¿cómo usaría bash para extraer solo el fizzbuzz porción de dicha cadena?

¿Fue útil?

Solución

Aquí se explica cómo hacerlo con los operadores # y % en Bash.

$ x="/foo/fizzbuzz.bar"
$ y=${x%.bar}
$ echo ${y##*/}
fizzbuzz

${x%.bar} también podría ser ${x%.*} para eliminar todo después de un punto o ${x%%.*} para eliminar todo después del primer punto.

Ejemplo:

$ x="/foo/fizzbuzz.bar.quux"
$ y=${x%.*}
$ echo $y
/foo/fizzbuzz.bar
$ y=${x%%.*}
$ echo $y
/foo/fizzbuzz

La documentación se puede encontrar en el manual de bash.Buscar ${parameter%word} y ${parameter%%word} sección de coincidencia de la porción final.

Otros consejos

mira el comando de nombre base:

NAME=$(basename /foo/fizzbuzz.bar .bar)

Bash puro, realizado en dos operaciones separadas:

  1. Elimine la ruta de una cadena de ruta:

    path=/foo/bar/bim/baz/file.gif
    
    file=${path##*/}  
    #$file is now 'file.gif'
    
  2. Elimine la extensión de una cadena de ruta:

    base=${file%.*}
    #${base} is now 'file'.
    

Usando el nombre base utilicé lo siguiente para lograr esto:

for file in *; do
    ext=${file##*.}
    fname=`basename $file $ext`

    # Do things with $fname
done;

Esto no requiere conocimiento a priori de la extensión del archivo y funciona incluso cuando tiene un nombre de archivo que tiene puntos en su nombre (delante de su extensión);requiere el programa basename Sin embargo, esto es parte de GNU coreutils, por lo que debería enviarse con cualquier distribución.

Manera bash pura:

~$ x="/foo/bar/fizzbuzz.bar.quux.zoom"; 
~$ y=${x/\/*\//}; 
~$ echo ${y/.*/}; 
fizzbuzz

Esta funcionalidad se explica en man bash en "Expansión de parámetros".Abundan las formas no bash:awk, perl, sed, etc.

EDITAR:Funciona con puntos en el archivo sufijos y no necesita saber el sufijo (extensión), pero no trabajar con puntos en el nombre sí mismo.

Las funciones basename y dirname son lo que buscas:

mystring=/foo/fizzbuzz.bar
echo basename: $(basename "${mystring}")
echo basename + remove .bar: $(basename "${mystring}" .bar)
echo dirname: $(dirname "${mystring}")

Tiene salida:

basename: fizzbuzz.bar
basename + remove .bar: fizzbuzz
dirname: /foo
perl -pe 's/\..*$//;s{^.*/}{}'

Usando basename Se supone que sabes cuál es la extensión del archivo, ¿no?

Y creo que las diversas sugerencias de expresiones regulares no funcionan con un nombre de archivo que contenga más de un "."

Lo siguiente parece hacer frente a los puntos dobles.Ah, y nombres de archivos que contienen un "/" (solo por diversión)

Parafraseando a Pascal: "Lo siento, este guión es tan largo.No tuve tiempo de hacerlo más corto"


  #!/usr/bin/perl
  $fullname = $ARGV[0];
  ($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
  ($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
  print $basename . "\n";
 

Si no puedes usar el nombre base como se sugiere en otras publicaciones, siempre puedes usar sed.He aquí un (feo) ejemplo.No es el mejor, pero funciona extrayendo la cadena deseada y reemplazando la entrada con la cadena deseada.

echo '/foo/fizzbuzz.bar' | sed 's|.*\/\([^\.]*\)\(\..*\)$|\1|g'

¿Cuál te dará el resultado?

fizzbuzz

Además de Sintaxis compatible con POSIX utilizada en esta respuesta,

basename cadena [sufijo]

como en

basename /foo/fizzbuzz.bar .bar

ÑU basename admite otra sintaxis:

basename -s .bar /foo/fizzbuzz.bar

con el mismo resultado.La diferencia y ventaja es que -s implica -a, que admite múltiples argumentos:

$ basename -s .bar /foo/fizzbuzz.bar /baz/foobar.bar
fizzbuzz
foobar

Esto incluso se puede hacer seguro para el nombre de archivo separando la salida con bytes NUL usando el -z opción, por ejemplo, para estos archivos que contienen espacios en blanco, nuevas líneas y caracteres globales (citados por ls):

$ ls has*
'has'$'\n''newline.bar'  'has space.bar'  'has*.bar'

Leyendo en una matriz:

$ readarray -d $'\0' arr < <(basename -zs .bar has*)
$ declare -p arr
declare -a arr=([0]=$'has\nnewline' [1]="has space" [2]="has*")

readarray -d requiere Bash 4.4 o posterior.Para versiones anteriores, tenemos que hacer un bucle:

while IFS= read -r -d '' fname; do arr+=("$fname"); done < <(basename -zs .bar has*)

Tenga cuidado con la solución Perl sugerida:elimina todo lo que esté después del primer punto.

$ echo some.file.with.dots | perl -pe 's/\..*$//;s{^.*/}{}'
some

Si quieres hacerlo con perl, esto funciona:

$ echo some.file.with.dots | perl -pe 's/(.*)\..*$/$1/;s{^.*/}{}'
some.file.with

Pero si estás usando Bash, las soluciones con y=${x%.*} (o basename "$x" .ext si conoces la extensión) son mucho más simples.

El nombre base hace eso, elimina la ruta.También eliminará el sufijo si se proporciona y si coincide con el sufijo del archivo, pero necesitará saber el sufijo para darle al comando.De lo contrario, puede usar mv y averiguar cuál debería ser el nuevo nombre de otra manera.

Combinando la respuesta mejor calificada con la segunda respuesta mejor calificada para obtener el nombre del archivo sin la ruta completa:

$ x="/foo/fizzbuzz.bar.quux"
$ y=(`basename ${x%%.*}`)
$ echo $y
fizzbuzz
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top