string - script - ¿Cómo elimino el sufijo de archivo y la parte de la ruta de acceso de una cadena de ruta en Bash?
scripts bash ejemplos (13)
Además de la sintaxis conforme con POSIX utilizada en esta respuesta ,
$ x="/foo/fizzbuzz.bar.quux"
$ y=(`basename ${x%%.*}`)
$ echo $y
fizzbuzz
como en
basename string [suffix]
El basename
GNU soporta otra sintaxis:
basename /foo/fizzbuzz.bar .bar
Con el mismo resultado. La diferencia y la ventaja es que -s
implica -a
, que admite múltiples argumentos:
basename -s .bar /foo/fizzbuzz.bar
Esto incluso se puede hacer seguro para el nombre de archivo separando la salida con bytes NUL usando la opción -z
, por ejemplo, para estos archivos que contienen espacios en blanco, líneas nuevas y caracteres globales (citados por ls
):
$ basename -s .bar /foo/fizzbuzz.bar /baz/foobar.bar
fizzbuzz
foobar
Leyendo en una matriz:
$ ls has*
''has''$''/n''''newline.bar'' ''has space.bar'' ''has*.bar''
readarray -d
requiere Bash 4.4 o posterior. Para versiones anteriores, tenemos que hacer un bucle:
$ readarray -d $''/0'' arr < <(basename -zs .bar has*)
$ declare -p arr
declare -a arr=([0]=$''has/nnewline'' [1]="has space" [2]="has*")
Dada una ruta de archivo de cadena como /foo/fizzbuzz.bar
, ¿cómo usaría bash para extraer solo la parte de fizzbuzz
de dicha cadena?
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 . Busque la sección de coincidencia de la parte final de ${parameter%word}
y ${parameter%%word}
.
Bash puro, hecho en dos operaciones separadas:
Eliminar la ruta de una cadena de ruta:
path=/foo/bar/bim/baz/file.gif file=${path##*/} #$file is now ''file.gif''
Eliminar la extensión de una cadena de ruta:
base=${file%.*} #${base} is now ''file''.
Combinando la respuesta con la mejor calificación con la segunda respuesta con la mejor calificación para obtener el nombre de archivo sin la ruta completa:
while IFS= read -r -d '''' fname; do arr+=("$fname"); done < <(basename -zs .bar has*)
Cuidado con la solución perl sugerida: elimina cualquier cosa 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á utilizando Bash, las soluciones con y=${x%.*}
(O basename "$x" .ext
si conoce la extensión) son mucho más simples.
El nombre base hace eso, elimina la ruta. También eliminará el sufijo si se da y si coincide con el sufijo del archivo, pero necesitará saber el sufijo para dar al comando. De lo contrario, puede usar mv y averiguar cuál debería ser el nuevo nombre de alguna otra manera.
El uso de basename
supone que sabes cuál es la extensión del archivo, ¿no es así?
Y creo que las diversas sugerencias de expresiones regulares no hacen frente a un nombre de archivo que contiene más de uno "."
Lo siguiente parece hacer frente a los puntos dobles. Ah, y los nombres de archivo que contienen una "/" (solo para patadas)
Parafraseando a Pascal: "Lo siento, este script 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";
Las funciones basename y dirname son las siguientes:
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
Puro camino de bash:
~$ 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 y así sucesivamente.
EDITAR: funciona con puntos en los sufijos de archivos y no necesita conocer el sufijo (extensión), pero no funciona con puntos en el nombre en sí.
Si no puedes usar el nombre base como se sugiere en otras publicaciones, siempre puedes usar sed. Aquí hay un ejemplo (feo). 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''
Lo que te dará la salida.
Fizzbuzz
Usando basename usé lo siguiente para lograr esto:
for file in *; do
ext=${file##*.}
fname=`basename $file $ext`
# Do things with $fname
done;
Esto no requiere un conocimiento a priori de la extensión de archivo y funciona incluso cuando tiene un nombre de archivo que tiene puntos en su nombre de archivo (delante de su extensión); Sin embargo, sí requiere el basename
del programa, pero esto es parte de los coreutils de GNU, por lo que debería enviarse con cualquier distro.
mira el comando basename:
NAME=`basename /foo/fizzbuzz.bar .bar`
perl -pe ''s//..*$//;s{^.*/}{}''