que - Extraer nombre de archivo y extensión en Bash
que es un script en bash (30)
Reconocimiento de archivos mágicos
Además de las muchas buenas respuestas en esta pregunta de desbordamiento de pila, me gustaría agregar:
Bajo Linux y otros unixen, hay un comando mágico llamado file
, que realiza la detección de tipo de archivo analizando algunos primeros bytes de archivo. Esta es una herramienta muy antigua, utilizada inicialmente para servidores de impresión (si no se creó para ... No estoy seguro de eso).
file myfile.txt
myfile.txt: UTF-8 Unicode text
file -b --mime-type myfile.txt
text/plain
Las extensiones de estándares se pueden encontrar en /etc/mime.types
(en mi escritorio Debian GNU / Linux. Ver man file
man mime.types
y man mime.types
. Quizás tenga que instalar la utilidad de file
y mime-support
paquetes de mime-support
):
grep $( file -b --mime-type myfile.txt ) </etc/mime.types
text/plain asc txt text pot brf srt
Podría crear una función bash para determinar la extensión correcta. Hay una pequeña muestra (no perfecta):
file2ext() {
local _mimetype=$(file -Lb --mime-type "$1") _line _basemimetype
case ${_mimetype##*[/.-]} in
gzip | bzip2 | xz | z )
_mimetype=${_mimetype##*[/.-]}
_mimetype=${_mimetype//ip}
_basemimetype=$(file -zLb --mime-type "$1")
;;
stream )
_mimetype=($(file -Lb "$1"))
[ "${_mimetype[1]}" = "compressed" ] &&
_basemimetype=$(file -b --mime-type - < <(
${_mimetype,,} -d <"$1")) ||
_basemimetype=${_mimetype,,}
_mimetype=${_mimetype,,}
;;
executable ) _mimetype='''' _basemimetype='''' ;;
dosexec ) _mimetype='''' _basemimetype=''exe'' ;;
shellscript ) _mimetype='''' _basemimetype=''sh'' ;;
* )
_basemimetype=$_mimetype
_mimetype=''''
;;
esac
while read -a _line ;do
if [ "$_line" == "$_basemimetype" ] ;then
[ "$_line[1]" ] &&
_basemimetype=${_line[1]} ||
_basemimetype=${_basemimetype##*[/.-]}
break
fi
done </etc/mime.types
case ${_basemimetype##*[/.-]} in
executable ) _basemimetype='''' ;;
shellscript ) _basemimetype=''sh'' ;;
dosexec ) _basemimetype=''exe'' ;;
* ) ;;
esac
[ "$_mimetype" ] && [ "$_basemimetype" != "$_mimetype" ] &&
printf ${2+-v} $2 "%s.%s" ${_basemimetype##*[/.-]} ${_mimetype##*[/.-]} ||
printf ${2+-v} $2 "%s" ${_basemimetype##*[/.-]}
}
Esta función podría establecer una variable Bash que se puede usar más adelante:
(Esto está inspirado en la respuesta correcta de @Petesh):
filename=$(basename "$fullfile")
filename="${filename%.*}"
file2ext "$fullfile" extension
echo "$fullfile -> $filename . $extension"
Quiero obtener el nombre de archivo (sin extensión) y la extensión por separado.
La mejor solución que encontré hasta ahora es:
NAME=`echo "$FILE" | cut -d''.'' -f1`
EXTENSION=`echo "$FILE" | cut -d''.'' -f2`
Esto es incorrecto porque no funciona si el nombre del archivo contiene múltiples .
caracteres. Si, digamos, tengo abjs
, considerará a
y b.js
, en lugar de ab
y js
.
Se puede hacer fácilmente en Python con
file, ext = os.path.splitext(path)
pero preferiría no encender un intérprete de Python solo por esto, si es posible.
¿Alguna idea mejor?
Aquí está el código con AWK . Se puede hacer de forma más sencilla. Pero no soy bueno en AWK.
filename$ ls
abc.a.txt a.b.c.txt pp-kk.txt
filename$ find . -type f | awk -F/ ''{print $2}'' | rev | awk -F"." ''{$1="";print}'' | rev | awk ''gsub(" ",".") ,sub(".$", "")''
abc.a
a.b.c
pp-kk
filename$ find . -type f | awk -F/ ''{print $2}'' | awk -F"." ''{print $NF}''
txt
txt
txt
Aquí hay algunas sugerencias alternativas (principalmente en awk
), incluidos algunos casos de uso avanzados, como extraer números de versión de paquetes de software.
f=''/path/to/complex/file.1.0.1.tar.gz''
# Filename : ''file.1.0.x.tar.gz''
echo "$f" | awk -F''/'' ''{print $NF}''
# Extension (last): ''gz''
echo "$f" | awk -F''[.]'' ''{print $NF}''
# Extension (all) : ''1.0.1.tar.gz''
echo "$f" | awk ''{sub(/[^.]*[.]/, "", $0)} 1''
# Extension (last-2): ''tar.gz''
echo "$f" | awk -F''[.]'' ''{print $(NF-1)"."$NF}''
# Basename : ''file''
echo "$f" | awk ''{gsub(/.*[/]|[.].*/, "", $0)} 1''
# Basename-extended : ''file.1.0.1.tar''
echo "$f" | awk ''{gsub(/.*[/]|[.]{1}[^.]+$/, "", $0)} 1''
# Path : ''/path/to/complex/''
echo "$f" | awk ''{match($0, /.*[/]/, a); print a[0]}''
# or
echo "$f" | grep -Eo ''.*[/]''
# Folder (containing the file) : ''complex''
echo "$f" | awk -F''/'' ''{$1=""; print $(NF-1)}''
# Version : ''1.0.1''
# Defined as ''number.number'' or ''number.number.number''
echo "$f" | grep -Eo ''[0-9]+[.]+[0-9]+[.]?[0-9]?''
# Version - major : ''1''
echo "$f" | grep -Eo ''[0-9]+[.]+[0-9]+[.]?[0-9]?'' | cut -d. -f1
# Version - minor : ''0''
echo "$f" | grep -Eo ''[0-9]+[.]+[0-9]+[.]?[0-9]?'' | cut -d. -f2
# Version - patch : ''1''
echo "$f" | grep -Eo ''[0-9]+[.]+[0-9]+[.]?[0-9]?'' | cut -d. -f3
# All Components : "path to complex file 1 0 1 tar gz"
echo "$f" | awk -F''[/.]'' ''{$1=""; print $0}''
# Is absolute : True (exit-code : 0)
# Return true if it is an absolute path (starting with ''/'' or ''~/''
echo "$f" | grep -q ''^[/]/|^~/''
Todos los casos de uso utilizan la ruta completa original como entrada, sin depender de los resultados intermedios.
Cómo extraer el nombre del archivo y la extensión en fish :
function split-filename-extension --description "Prints the filename and extension"
for file in $argv
if test -f $file
set --local extension (echo $file | awk -F. ''{print $NF}'')
set --local filename (basename $file .$extension)
echo "$filename $extension"
else
echo "$file is not a valid file"
end
end
end
Advertencias: se divide en el último punto, que funciona bien para los nombres de archivo con puntos en ellos, pero no para extensiones con puntos en ellos. Vea el ejemplo a continuación.
Uso:
$ split-filename-extension foo-0.4.2.zip bar.tar.gz
foo-0.4.2 zip # Looks good!
bar.tar gz # Careful, you probably want .tar.gz as the extension.
Probablemente hay mejores maneras de hacer esto. Siéntete libre de editar mi respuesta para mejorarla.
Si hay un conjunto limitado de extensiones con las que tratará y conoce todas ellas, intente esto:
switch $file
case *.tar
echo (basename $file .tar) tar
case *.tar.bz2
echo (basename $file .tar.bz2) tar.bz2
case *.tar.gz
echo (basename $file .tar.gz) tar.gz
# and so on
end
Esto no tiene la advertencia como el primer ejemplo, pero sí tiene que manejar cada caso para que sea más tedioso dependiendo de cuántas extensiones pueda esperar.
Creo que si solo necesitas el nombre del archivo, puedes intentar esto:
FULLPATH=/usr/share/X11/xorg.conf.d/50-synaptics.conf
# Remove all the prefix until the "/" character
FILENAME=${FULLPATH##*/}
# Remove all the prefix until the "." character
FILEEXTENSION=${FILENAME##*.}
# Remove a suffix, in our case, the filename. This will return the name of the directory that contains this file.
BASEDIRECTORY=${FULLPATH%$FILENAME}
echo "path = $FULLPATH"
echo "file name = $FILENAME"
echo "file extension = $FILEEXTENSION"
echo "base directory = $BASEDIRECTORY"
Y eso es todo = D.
Eso no parece funcionar si el archivo no tiene extensión o no tiene nombre de archivo. Esto es lo que estoy usando; solo utiliza elementos incorporados y maneja más nombres de archivos patológicos (pero no todos).
#!/bin/bash
for fullpath in "$@"
do
filename="${fullpath##*/}" # Strip longest match of */ from start
dir="${fullpath:0:${#fullpath} - ${#filename}}" # Substring from 0 thru pos of filename
base="${filename%.[^.]*}" # Strip shortest match of . plus at least one non-dot char from end
ext="${filename:${#base} + 1}" # Substring from len of base thru end
if [[ -z "$base" && -n "$ext" ]]; then # If we have an extension and no base, it''s really the base
base=".$ext"
ext=""
fi
echo -e "$fullpath:/n/tdir = /"$dir/"/n/tbase = /"$base/"/n/text = /"$ext/""
done
Y aquí están algunos casos de prueba:
$ basename-and-extension.sh / /home/me/ /home/me/file /home/me/file.tar /home/me/file.tar.gz /home/me/.hidden /home/me/.hidden.tar /home/me/.. . /: dir = "/" base = "" ext = "" /home/me/: dir = "/home/me/" base = "" ext = "" /home/me/file: dir = "/home/me/" base = "file" ext = "" /home/me/file.tar: dir = "/home/me/" base = "file" ext = "tar" /home/me/file.tar.gz: dir = "/home/me/" base = "file.tar" ext = "gz" /home/me/.hidden: dir = "/home/me/" base = ".hidden" ext = "" /home/me/.hidden.tar: dir = "/home/me/" base = ".hidden" ext = "tar" /home/me/..: dir = "/home/me/" base = ".." ext = "" .: dir = "" base = "." ext = ""
La solución más pequeña y simple (en una sola línea) es:
$ file=/blaabla/bla/blah/foo.txt
echo $(basename ${file%.*}) # foo
Mellen escribe en un comentario en una publicación de blog:
Al usar Bash, también hay ${file%.*}
Para obtener el nombre del archivo sin la extensión y ${file##*.}
Para obtener la extensión solo. Es decir,
file="thisfile.txt"
echo "filename: ${file%.*}"
echo "extension: ${file##*.}"
Salidas:
filename: thisfile
extension: txt
No hay necesidad de molestarse con awk
o sed
o incluso perl
para esta tarea simple. Existe una solución compatible con os.path.splitext()
Bash, os.path.splitext()
que solo usa expansiones de parámetros.
Implementación de referencia
Documentación de os.path.splitext(path)
:
Divida la ruta del nombre de ruta en un par
(root, ext)
manera queroot + ext == path
, y ext esté vacío o comience con un período y contenga como máximo un período. Los periodos iniciales en el nombre base se ignoran;splitext(''.cshrc'')
devuelve(''.cshrc'', '''')
.
Código Python:
root, ext = os.path.splitext(path)
Implementacion de Bash
Honrando periodos principales
root="${path%.*}"
ext="${path#"$root"}"
Ignorando los periodos iniciales
root="${path#.}";root="${path%"$root"}${root%.*}"
ext="${path#"$root"}"
Pruebas
Aquí hay casos de prueba para la implementación de ignorar los periodos iniciales, que debe coincidir con la implementación de referencia de Python en cada entrada
|---------------|-----------|-------|
|path |root |ext |
|---------------|-----------|-------|
|'' .txt'' |'' '' |''.txt'' |
|'' .txt.txt'' |'' .txt'' |''.txt'' |
|'' txt'' |'' txt'' |'''' |
|''*.txt.txt'' |''*.txt'' |''.txt'' |
|''.cshrc'' |''.cshrc'' |'''' |
|''.txt'' |''.txt'' |'''' |
|''?.txt.txt'' |''?.txt'' |''.txt'' |
|''/n.txt.txt'' |''/n.txt'' |''.txt'' |
|''/t.txt.txt'' |''/t.txt'' |''.txt'' |
|''a b.txt.txt'' |''a b.txt'' |''.txt'' |
|''a*b.txt.txt'' |''a*b.txt'' |''.txt'' |
|''a?b.txt.txt'' |''a?b.txt'' |''.txt'' |
|''a/nb.txt.txt'' |''a/nb.txt'' |''.txt'' |
|''a/tb.txt.txt'' |''a/tb.txt'' |''.txt'' |
|''txt'' |''txt'' |'''' |
|''txt.pdf'' |''txt'' |''.pdf'' |
|''txt.tar.gz'' |''txt.tar'' |''.gz'' |
|''txt.txt'' |''txt'' |''.txt'' |
|---------------|-----------|-------|
Resultados de la prueba
Todas las pruebas pasaron.
Ok, si entiendo bien, el problema aquí es cómo obtener el nombre y la extensión completa de un archivo que tiene varias extensiones, por ejemplo, stuff.tar.gz
.
Esto funciona para mí:
fullfile="stuff.tar.gz"
fileExt=${fullfile#*.}
fileName=${fullfile%*.$fileExt}
Esto te dará stuff
como nombre de archivo y .tar.gz
como extensión. Funciona para cualquier número de extensiones, incluyendo 0. Espero que esto ayude a cualquier persona que tenga el mismo problema =)
Podría usar el comando de cut
para eliminar las dos últimas extensiones (la parte ".tar.gz"
):
$ echo "foo.tar.gz" | cut -d''.'' --complement -f2-
foo
Como lo señaló Clayton Hughes en un comentario, esto no funcionará para el ejemplo real en la pregunta. Entonces, como alternativa, propongo usar sed
con expresiones regulares extendidas, como esta:
$ echo "mpc-1.0.1.tar.gz" | sed -r ''s//.[[:alnum:]]+/.[[:alnum:]]+$//''
mpc-1.0.1
Funciona eliminando las últimas dos extensiones (alfanuméricas) de manera incondicional.
[Actualizado de nuevo después de un comentario de Anders Lindahl]
Por lo general, ya conoces la extensión, por lo que es posible que desees utilizar:
basename filename .extension
por ejemplo:
basename /path/to/dir/filename.txt .txt
y obtenemos
filename
Primero, obtenga el nombre del archivo sin la ruta:
filename=$(basename -- "$fullfile")
extension="${filename##*.}"
filename="${filename%.*}"
Alternativamente, puedes enfocarte en el último ''/'' del camino en lugar del ''.'' que debería funcionar incluso si tiene extensiones de archivo impredecibles:
filename="${fullfile##*/}"
Es posible que desee comprobar la documentación:
- En la web en la sección " 3.5.3 Expansión de parámetros de shell "
- En la página de manual de bash en la sección llamada "Expansión de parámetros"
Puede forzar el corte para mostrar todos los campos y los posteriores agregando al número de campo.
NAME=`basename "$FILE"`
EXTENSION=`echo "$NAME" | cut -d''.'' -f2-`
Entonces, si ARCHIVO es eth0.pcap.gz
, la EXTENSIÓN será pcap.gz
Usando la misma lógica, también puede obtener el nombre del archivo usando ''-'' con el siguiente corte:
NAME=`basename "$FILE" | cut -d''.'' -f-1`
Esto funciona incluso para los nombres de archivo que no tienen ninguna extensión.
Puedes usar
sed ''s/^/./'' | rev | cut -d. -f2- | rev | cut -c2-
para obtener el nombre del archivo y
sed ''s/^/./'' | rev | cut -d. -f1 | rev
para obtener la extensión.
Caso de prueba:
echo "filename.gz" | sed ''s/^/./'' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename.gz" | sed ''s/^/./'' | rev | cut -d. -f1 | rev
echo "filename" | sed ''s/^/./'' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename" | sed ''s/^/./'' | rev | cut -d. -f1 | rev
echo "filename.tar.gz" | sed ''s/^/./'' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename.tar.gz" | sed ''s/^/./'' | rev | cut -d. -f1 | rev
Puedes usar el basename
.
Ejemplo:
$ basename foo-bar.tar.gz .tar.gz
foo-bar
Debe proporcionar el nombre base con la extensión que se eliminará, sin embargo, si siempre está ejecutando tar
con -z
entonces sabe que la extensión será .tar.gz
.
Esto debería hacer lo que quieras:
tar -zxvf $1
cd $(basename $1 .tar.gz)
Puedes usar la magia de las variables POSIX:
bash-3.2$ FILENAME=somefile.tar.gz
bash-3.2$ echo ${FILENAME%%.*}
somefile
bash-3.2$ echo ${FILENAME%.*}
somefile.tar
Hay una advertencia en que si su nombre de archivo tenía la forma ./somefile.tar.gz
entonces echo ${FILENAME%%.*}
Eliminaría con avidez la coincidencia más larga con .
y tendrías la cadena vacía.
(Puedes solucionar esto con una variable temporal:
FULL_FILENAME=$FILENAME
FILENAME=${FULL_FILENAME##*/}
echo ${FILENAME%%.*}
)
Este site explica más.
${variable%pattern}
Trim the shortest match from the end
${variable##pattern}
Trim the longest match from the beginning
${variable%%pattern}
Trim the longest match from the end
${variable#pattern}
Trim the shortest match from the beginning
Una respuesta simple:
Para ampliar la respuesta de las variables POSIX , tenga en cuenta que puede hacer más patrones interesantes. Así que para el caso detallado aquí, simplemente podría hacer esto:
tar -zxvf $1
cd ${1%.tar.*}
Eso cortará la última aparición de .tar. <algo> .
Más en general, si desea eliminar la última aparición de. <algo> . <algo-else> entonces
${1.*.*}
debería funcionar bien
El enlace de la respuesta anterior parece estar muerto. Aquí hay una gran explicación de un montón de manipulación de cadenas que puede hacer directamente en Bash, desde TLDP .
Yo uso el siguiente script
$ echo "foo.tar.gz"|rev|cut -d"." -f3-|rev
foo
[Revisado de una función bash a una genérica de bash, el comportamiento ahora es consistente con las utilidades dirname
y basename
; justificación añadida.]
La respuesta aceptada funciona bien en casos típicos , pero falla en casos de borde , a saber:
- Para los nombres de archivo sin extensión (llamado sufijo en el resto de esta respuesta),
extension=${filename##*.}
Devuelve el nombre de archivo de entrada en lugar de una cadena vacía. -
extension=${filename##*.}
no incluye la inicial.
, contrario a lo convencional.- Preparando a ciegas
.
no funcionaría para los nombres de archivo sin sufijo.
- Preparando a ciegas
-
filename="${filename%.*}"
será la cadena vacía, si el nombre del archivo de entrada comienza por.
y no contiene más.
caracteres (por ejemplo,.bash_profile
) - contrariamente a la convención.
---------
Por lo tanto, la complejidad de una solución robusta que cubre todos los casos perimetrales requiere una función ; consulte su definición a continuación; Puede devolver todos los componentes de un camino .
Ejemplo de llamada:
splitPath ''/etc/bash.bashrc'' dir fname fnameroot suffix
# -> $dir == ''/etc''
# -> $fname == ''bash.bashrc''
# -> $fnameroot == ''bash''
# -> $suffix == ''.bashrc''
Tenga en cuenta que los argumentos después de la ruta de entrada se eligen libremente, nombres de variables posicionales.
Para omitir las variables que no sean de interés antes de las que están, especifique _
(para usar la variable $_
) o ''''
; por ejemplo, para extraer la raíz y la extensión del nombre de archivo solamente, use splitPath ''/etc/bash.bashrc'' _ _ fnameroot extension
.
# SYNOPSIS
# splitPath path varDirname [varBasename [varBasenameRoot [varSuffix]]]
# DESCRIPTION
# Splits the specified input path into its components and returns them by assigning
# them to variables with the specified *names*.
# Specify '''' or throw-away variable _ to skip earlier variables, if necessary.
# The filename suffix, if any, always starts with ''.'' - only the *last*
# ''.''-prefixed token is reported as the suffix.
# As with `dirname`, varDirname will report ''.'' (current dir) for input paths
# that are mere filenames, and ''/'' for the root dir.
# As with `dirname` and `basename`, a trailing ''/'' in the input path is ignored.
# A ''.'' as the very first char. of a filename is NOT considered the beginning
# of a filename suffix.
# EXAMPLE
# splitPath ''/home/jdoe/readme.txt'' parentpath fname fnameroot suffix
# echo "$parentpath" # -> ''/home/jdoe''
# echo "$fname" # -> ''readme.txt''
# echo "$fnameroot" # -> ''readme''
# echo "$suffix" # -> ''.txt''
# ---
# splitPath ''/home/jdoe/readme.txt'' _ _ fnameroot
# echo "$fnameroot" # -> ''readme''
splitPath() {
local _sp_dirname= _sp_basename= _sp_basename_root= _sp_suffix=
# simple argument validation
(( $# >= 2 )) || { echo "$FUNCNAME: ERROR: Specify an input path and at least 1 output variable name." >&2; exit 2; }
# extract dirname (parent path) and basename (filename)
_sp_dirname=$(dirname "$1")
_sp_basename=$(basename "$1")
# determine suffix, if any
_sp_suffix=$([[ $_sp_basename = *.* ]] && printf %s ".${_sp_basename##*.}" || printf '''')
# determine basename root (filemane w/o suffix)
if [[ "$_sp_basename" == "$_sp_suffix" ]]; then # does filename start with ''.''?
_sp_basename_root=$_sp_basename
_sp_suffix=''''
else # strip suffix from filename
_sp_basename_root=${_sp_basename%$_sp_suffix}
fi
# assign to output vars.
[[ -n $2 ]] && printf -v "$2" "$_sp_dirname"
[[ -n $3 ]] && printf -v "$3" "$_sp_basename"
[[ -n $4 ]] && printf -v "$4" "$_sp_basename_root"
[[ -n $5 ]] && printf -v "$5" "$_sp_suffix"
return 0
}
test_paths=(
''/etc/bash.bashrc''
''/usr/bin/grep''
''/Users/jdoe/.bash_profile''
''/Library/Application Support/''
''readme.new.txt''
)
for p in "${test_paths[@]}"; do
echo ----- "$p"
parentpath= fname= fnameroot= suffix=
splitPath "$p" parentpath fname fnameroot suffix
for n in parentpath fname fnameroot suffix; do
echo "$n=${!n}"
done
done
Código de prueba que ejerce la función:
test_paths=(
''/etc/bash.bashrc''
''/usr/bin/grep''
''/Users/jdoe/.bash_profile''
''/Library/Application Support/''
''readme.new.txt''
)
for p in "${test_paths[@]}"; do
echo ----- "$p"
parentpath= fname= fnameroot= suffix=
splitPath "$p" parentpath fname fnameroot suffix
for n in parentpath fname fnameroot suffix; do
echo "$n=${!n}"
done
done
Salida esperada - tenga en cuenta los casos de borde:
- un nombre de archivo sin sufijo
- un nombre de archivo que comienza con
.
( no se considera el inicio del sufijo) - una ruta de entrada que termina en
/
(al final/
se ignora) - una ruta de entrada que es solo un nombre de archivo (
.
se devuelve como la ruta principal) - un nombre de archivo que tiene más de
.
token prefijado (solo el último se considera el sufijo):
----- /etc/bash.bashrc
parentpath=/etc
fname=bash.bashrc
fnameroot=bash
suffix=.bashrc
----- /usr/bin/grep
parentpath=/usr/bin
fname=grep
fnameroot=grep
suffix=
----- /Users/jdoe/.bash_profile
parentpath=/Users/jdoe
fname=.bash_profile
fnameroot=.bash_profile
suffix=
----- /Library/Application Support/
parentpath=/Library
fname=Application Support
fnameroot=Application Support
suffix=
----- readme.new.txt
parentpath=.
fname=readme.new.txt
fnameroot=readme.new
suffix=.txt
Aquí está el algoritmo que utilicé para encontrar el nombre y la extensión de un archivo cuando escribí un script Bash para hacer nombres únicos cuando los nombres estaban en conflicto con respecto a la carcasa.
#! /bin/bash
#
# Finds
# -- name and extension pairs
# -- null extension when there isn''t an extension.
# -- Finds name of a hidden file without an extension
#
declare -a fileNames=(
''.Montreal''
''.Rome.txt''
''Loundon.txt''
''Paris''
''San Diego.txt''
''San Francisco''
)
echo "Script ${0} finding name and extension pairs."
echo
for theFileName in "${fileNames[@]}"
do
echo "theFileName=${theFileName}"
# Get the proposed name by chopping off the extension
name="${theFileName%.*}"
# get extension. Set to null when there isn''t an extension
# Thanks to mklement0 in a comment above.
extension=$([[ "$theFileName" == *.* ]] && echo ".${theFileName##*.}" || echo '''')
# a hidden file without extenson?
if [ "${theFileName}" = "${extension}" ] ; then
# hidden file without extension. Fixup.
name=${theFileName}
extension=""
fi
echo " name=${name}"
echo " extension=${extension}"
done
La prueba de ejecución.
$ config/Name/&Extension.bash
Script config/Name&Extension.bash finding name and extension pairs.
theFileName=.Montreal
name=.Montreal
extension=
theFileName=.Rome.txt
name=.Rome
extension=.txt
theFileName=Loundon.txt
name=Loundon
extension=.txt
theFileName=Paris
name=Paris
extension=
theFileName=San Diego.txt
name=San Diego
extension=.txt
theFileName=San Francisco
name=San Francisco
extension=
$
FYI: El programa completo de transliteración y más casos de prueba se pueden encontrar aquí: https://www.dropbox.com/s/4c6m0f2e28a1vxf/avoid-clashes-code.zip?dl=0
De las respuestas anteriores, el oneliner más corto para imitar a Python''s
file, ext = os.path.splitext(path)
Suponiendo que su archivo realmente tiene una extensión, es
EXT="${PATH##*.}"; FILE=$(basename "$PATH" .$EXT)
Para hacer que dir sea más útil (en el caso de que se especifique un archivo local sin ruta), hice lo siguiente:
# Substring from 0 thru pos of filename
dir="${fullpath:0:${#fullpath} - ${#filename}}"
if [[ -z "$dir" ]]; then
dir="./"
fi
Esto le permite hacer algo útil como agregar un sufijo al nombre base del archivo de entrada como:
outfile=${dir}${base}_suffix.${ext}
testcase: foo.bar
dir: "./"
base: "foo"
ext: "bar"
outfile: "./foo_suffix.bar"
testcase: /home/me/foo.bar
dir: "/home/me/"
base: "foo"
ext: "bar"
outfile: "/home/me/foo_suffix.bar"
Simplemente usar ${parameter%word}
En tu caso:
${FILE%.*}
Si desea probarlo, todo lo siguiente funciona, y simplemente elimine la extensión:
FILE=abc.xyz; echo ${FILE%.*};
FILE=123.abc.xyz; echo ${FILE%.*};
FILE=abc; echo ${FILE%.*};
Basado en gran parte de los excelentes bashismos útiles de @ mklement0, y repleto de bashismos útiles , así como otras respuestas a esta / otras preguntas / "esa maldita Internet" ... Lo envolví todo en un poco, un poco más comprensible, función reutilizable para mi (o la tuya) .bash_profile
que se ocupa de lo que (considero) debería ser una versión más robusta de dirname
/ basename
/ lo que tienes ..
function path { SAVEIFS=$IFS; IFS="" # stash IFS for safe-keeping, etc.
[[ $# != 2 ]] && echo "usage: path <path> <dir|name|fullname|ext>" && return # demand 2 arguments
[[ $1 =~ ^(.*/)?(.+)?$ ]] && { # regex parse the path
dir=${BASH_REMATCH[1]}
file=${BASH_REMATCH[2]}
ext=$([[ $file = *.* ]] && printf %s ${file##*.} || printf '''')
# edge cases for extesionless files and files like ".nesh_profile.coffee"
[[ $file == $ext ]] && fnr=$file && ext='''' || fnr=${file:0:$((${#file}-${#ext}))}
case "$2" in
dir) echo "${dir%/*}"; ;;
name) echo "${fnr%.*}"; ;;
fullname) echo "${fnr%.*}.$ext"; ;;
ext) echo "$ext"; ;;
esac
}
IFS=$SAVEIFS
}
Ejemplos de uso ...
SOMEPATH=/path/to.some/.random/ file.gzip
path $SOMEPATH dir # /path/to.some
path $SOMEPATH name # .random file
path $SOMEPATH ext # gzip
path $SOMEPATH fullname # .random file.gzip
path gobbledygook # usage: -bash <path> <dir|name|fullname|ext>
Petesh partir de la respuesta de Petesh , si solo se necesita el nombre de archivo, tanto la ruta como la extensión se pueden eliminar en una sola línea,
filename=$(basename ${fullname%.*})
Si también quieres permitir extensiones vacías , este es el más corto que podría encontrar:
echo ''hello.txt'' | sed -r ''s/.+/.(.+)|.*//1/'' # EXTENSION
echo ''hello.txt'' | sed -r ''s/(.+)/..+|(.*)//1/2/'' # FILENAME
1ª línea explicada: coincide con PATH.EXT o CUALQUIER COSA y la reemplaza con EXT. Si se coincidió con CUALQUIER COSA, el grupo ext no se captura.
$ F = "text file.test.txt"
$ echo ${F/*./}
txt
Esto satisface múltiples puntos y espacios en un nombre de archivo, sin embargo, si no hay una extensión, devuelve el nombre de archivo en sí. Aunque es fácil de verificar; solo prueba para que el nombre de archivo y la extensión sean los mismos.
Naturalmente, este método no funciona para archivos .tar.gz. Sin embargo, eso podría ser manejado en un proceso de dos pasos. Si la extensión es gz, verifique de nuevo para ver si también hay una extensión tar.
pax> echo a.b.js | sed ''s//.[^.]*$//''
a.b
pax> echo a.b.js | sed ''s/^.*/.//''
js
Funciona bien, así que solo puedes usar:
pax> FILE=a.b.js
pax> NAME=$(echo "$FILE" | sed ''s//.[^.]*$//'')
pax> EXTENSION=$(echo "$FILE" | sed ''s/^.*/.//'')
pax> echo $NAME
a.b
pax> echo $EXTENSION
js
Los comandos, por cierto, funcionan de la siguiente manera.
El comando para NAME
sustituye a "."
carácter seguido de cualquier número de "."
no "."
caracteres hasta el final de la línea, sin nada (es decir, elimina todo desde el final "."
hasta el final de la línea, inclusive). Esto es básicamente una sustitución no codiciosa utilizando el engaño de expresiones regulares.
El comando para EXTENSION
sustituye a cualquier número de caracteres seguidos de "."
carácter al comienzo de la línea, sin nada (es decir, elimina todo desde el inicio de la línea hasta el punto final, inclusive). Esta es una sustitución codiciosa que es la acción por defecto.
~% FILE="example.tar.gz"
~% echo "${FILE%%.*}"
example
~% echo "${FILE%.*}"
example.tar
~% echo "${FILE#*.}"
tar.gz
~% echo "${FILE##*.}"
gz
Para más detalles, vea la expansión de parámetros de shell en el manual de Bash.