He visto guías de scripts de Bash que sugieren el uso de una matriz para trabajar con nombres de archivos que contienen espacios en blanco. Sin embargo, DashAsBinSh sugiere que las matrices no son portátiles, por lo que estoy buscando una forma de trabajar compatible con POSIX con listas de nombres de archivos que pueden contener espacios en blanco.
Estoy buscando modificar el script de ejemplo a continuación para que pueda echo
foo/target/a.jar
foo/target/b.jar
bar/target/lol whitespace.jar
Aquí está el guión
#!/usr/bin/env sh
INPUT="foo/target/a.jar
foo/target/b.jar
bar/target/b.jar
bar/target/lol whitespace.jar"
# this would be produced by a 'ls' command
# We can execute the ls within the script, if it helps
dostuffwith() { echo $1; };
F_LOCATIONS=$INPUT
ALL_FILES=$(for f in $F_LOCATIONS; do echo `basename $f`; done)
ALL_FILES=$(echo "$ALL_FILES" | sort | uniq)
for f in $ALL_FILES
do
fpath=$(echo "$F_LOCATIONS" | grep -m1 $f)
dostuffwith $fpath
done
shell-script
filenames
quoting
posix
whitespace
Eero Aaltonen
fuente
fuente
Respuestas:
Conchas POSIX tienen una matriz: los parámetros posicionales (
$1
,$2
, etc., refered colectivamente como"$@"
).Esto es inconveniente porque solo hay uno y destruye cualquier otro uso de los parámetros posicionales. Los parámetros posicionales son locales para una función, que a veces es una bendición y otras una maldición.
Si se garantiza que los nombres de sus archivos no contienen nuevas líneas, puede usar nuevas líneas como separador. Cuando expande la variable, primero desactive el globbing con
set -f
y configure la lista de caracteres de división de campoIFS
para que contenga solo una nueva línea.Con elementos en su lista separados por nuevas líneas, puede usar muchos comandos de procesamiento de texto de manera útil, en particular
sort
.Recuerde siempre poner comillas dobles alrededor de las sustituciones de variables, excepto cuando desee explícitamente que se produzca la división de campos (así como el bloqueo, a menos que lo haya desactivado).
fuente
sort | uniq
paso original funcione según lo previsto.Como su
$INPUT
variable usa líneas nuevas como separadores, voy a suponer que sus archivos no tendrán líneas nuevas en los nombres. Como tal, sí, hay una manera simple de iterar sobre los archivos y preservar los espacios en blanco.La idea es usar el
read
shell incorporado. Normalmenteread
se dividirá en cualquier espacio en blanco, por lo que los espacios lo dividirán. Pero puede configurarIFS=$'\n'
y se dividirá en líneas nuevas solamente. Para que pueda iterar sobre cada línea en su lista.Aquí está la solución más pequeña que se me ocurrió:
Básicamente, envía "$ INPUT" a la
awk
cual se deduplica el nombre del archivo (se divide/
y luego imprime la línea si el último elemento no se ha visto antes). Luego, una vez que awk ha generado la lista de rutas de archivos, usamoswhile read
para recorrer la lista.fuente
while
,dostuffwith
tenga en cuenta que con este cambio, el bucle y, por lo tanto, se ejecuta en una subshell. Por lo tanto, cualquier variable o cambio realizado en el shell en ejecución se perderá cuando se complete el ciclo. La única alternativa es usar un heredoc completo, que no es tan desagradable, pero pensé que sería preferible.IFS="\n"
se divide en barra invertida yn caracteres. Pero enread file
, no hay división.IFS="\n"
sigue siendo útil porque elimina los caracteres en blanco de $ IFS que, de lo contrario, se habrían eliminado al principio y al final de la entrada. Para leer una línea, la sintaxis canónica esIFS= read -r line
, sin embargoIFS=anything read -r line
(siempre que todo lo que no contenga espacios en blanco) funcione también.