Dada una ruta de archivo de cadena como /foo/fizzbuzz.bar
, ¿cómo usaría bash para extraer solo la fizzbuzz
parte de dicha cadena?
Dada una ruta de archivo de cadena como /foo/fizzbuzz.bar
, ¿cómo usaría bash para extraer solo la fizzbuzz
parte de dicha cadena?
Respuestas:
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%.*}
eliminar todo después de un punto o ${x%%.*}
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 ${parameter%word}
y ${parameter%%word}
rastree la sección correspondiente de la porción.
mira el comando basename:
NAME="$(basename /foo/fizzbuzz.bar .bar)"
Usando basename utilicé 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 del 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, requiere el programa basename
, pero esto es parte de los coreutils de GNU, por lo que debe enviarse con cualquier distribución.
fname=`basename $file .$ext`
Pura manera 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, etc.
EDITAR: funciona con puntos en sufijos de archivo y no necesita conocer el sufijo (extensión), pero no funciona con puntos en el nombre 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
mystring=$1
el valor constante actual en lugar del valor constante (que suprimirá varias advertencias, asegurándose de no contener espacios / caracteres globales / etc.) y aborde los problemas. encuentra?
echo "basename: $(basename "$mystring")"
esa manera, si mystring='/foo/*'
no se *
reemplaza con una lista de archivos en el directorio actual después de basename
finalizar.
Usar basename
supone que sabes cuál es la extensión del archivo, ¿no?
Y creo que las diversas sugerencias de expresiones regulares no hacen frente a un nombre de archivo que contiene más de un "."
Lo siguiente parece hacer frente a los puntos dobles. Ah, y los nombres de archivo que contienen un "/" en sí (solo por diversión)
Parafraseando a Pascal, "Lo siento, este guión es tan largo. No tuve tiempo para acortarlo"
#!/usr/bin/perl
$fullname = $ARGV[0];
($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
print $basename . "\n";
Además de la sintaxis conforme POSIX utilizada en esta respuesta ,
basename string [suffix]
como en
basename /foo/fizzbuzz.bar .bar
GNUbasename
admite otra sintaxis:
basename -s .bar /foo/fizzbuzz.bar
con el mismo resultado La diferencia y la ventaja es que eso -s
implica -a
, que admite múltiples argumentos:
$ basename -s .bar /foo/fizzbuzz.bar /baz/foobar.bar
fizzbuzz
foobar
Esto incluso puede hacerse seguro para el nombre de archivo separando la salida con bytes NUL usando la -z
opción, por ejemplo, para estos archivos que contienen espacios en blanco, líneas nuevas y caracteres globales (entre comillas ls
):
$ ls has*
'has'$'\n''newline.bar' 'has space.bar' 'has*.bar'
Lectura 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*)
Si no puede usar basename como se sugiere en otras publicaciones, siempre puede 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'
Que te dará la salida
fizzbuzz
Tenga 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 el camino. También eliminará el sufijo si se proporciona y si coincide con el sufijo del archivo, pero necesitaría saber el sufijo para darle el comando. De lo contrario, puede usar mv y descubrir 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 de archivo sin la ruta completa:
$ x="/foo/fizzbuzz.bar.quux"
$ y=(`basename ${x%%.*}`)
$ echo $y
fizzbuzz
${parameter%word}
y${parameter%%word}
en la sección de coincidencia de la parte posterior.