Construyendo caminos robustamente

16

Digamos que tengo varias variables en un script de shell (por ejemplo, en zsh):

FOLDER_1, FOLDER_2, etc.

Estas variables se refieren a carpetas que descienden de /. Por ejemplo, si tengo un camino/home/me/stuff/items

las variables serían:

FOLDER_1='home'
FOLDER_2='me'
FOLDER_3='stuff'

Ahora, digamos que quiero reconstruir la ruta correspondiente concatenando las variables. Una forma posible es construir la ruta de la siguiente manera:

PATH=$FOLDER_1/$FOLDER_2/$FOLDER_3/

Sin embargo, digamos que algunas de las variables FOLDER_ivienen con barras diagonales finales, mientras que otras no (y no sabemos cuáles), por ejemplo

FOLDER_1='home'
FOLDER_2='stuff/'
FOLDER_3='items'

Mi pregunta es: ¿cómo podría construir el camino de manera sólida? (por ejemplo, evitando barras dobles y agregándolas donde deben estar).

Pensé que una forma de hacerlo es agregar el /siempre entre pares de variables y luego eliminar cualquier duplicado con sed, pero no puedo hacer que funcione (no estoy seguro de estar manejando/ correctamente sed).

También, estoy reinventando la rueda ? (es decir, ¿hay algún elemento integrado que ya lo haga?).

Finalmente, si las variables están en una matriz , por ejemplo FOLDERS, ¿sería posible hacer esto sin bucle? (o, alternativamente, haciendo un bucle pero sin saber cuántos FOLDERShay en la matriz).

Amelio Vazquez-Reina
fuente

Respuestas:

12

Puede usar printfcon una matriz:

parts=("$FOLDER_1" "$FOLDER_2" "$FOLDER_3");
printf '/%s' "${parts[@]%/}"
# Use "printf -v path" to save it into a variable called "path" instead of printing it

El %operador recorta las cadenas finales, en este caso /. Al aplicarlo a parts[@], recorta cada miembro de la matriz por separado.

La clave para entender este printftruco es este fragmento de man 1 printf: "La cadena de formato se reutiliza con la frecuencia necesaria para satisfacer los argumentos".

janmoesen
fuente
El %método único funciona para la barra inclinada final mencionada en el queston. Para atender cuando hay múltiples barras, ${parts[@]%%/*}funciona. Aquí hay un enlace a un poco más de información sobre el problema de la barra: ¿Qué significan las barras dobles en la ruta UNIX? Es 'cd dir / subdir // válido ...
Peter.O
2
@fered: /*en este caso no significa cero o más barras , sino una barra seguida de cualquier número de caracteres. ¡Eso significa que si su ruta comienza con una barra oblicua, el resultado será la cadena vacía!
l0b0
Había considerado, según el ejemplo, que solo habría barras diagonales. Sin embargo, para adaptarse a la posibilidad de una barra inclinada (s), bash extglob(con expresiones regulares) puede usarse ... shopt -s extglob; ${parts[@]%%/+(/)}...
Peter.O
15

La respuesta simple es dejar de preocuparse y amar múltiples cortes. Las barras múltiples tienen el mismo efecto que una barra simple (excepto que una ruta que comienza //tiene un significado diferente en algunos sistemas; las capas de emulación de Unix en Windows son las únicas que puedo nombrar). Eso es por diseño, y poder ensamblar nombres de archivos sin tener que preocuparse por las barras múltiples fue una parte importante de esa decisión de diseño.

Para unir elementos de matriz, en zsh, puede usar el j indicador de expansión de parámetros .

dir=${(j:/:)FOLDERS}

Puede aplastar las barras duplicadas mientras lo hace, pero eso es puramente cosmético.

setopt extended_glob
dir=${${(j:/:)FOLDERS}//\/\/##/\/}

En ksh y bash, puede unir una matriz utilizando el primer carácter de $IFScomo separador. Luego puede aplastar las barras duplicadas. En bash, deberás hacer shopt -s extglobpara habilitar los globos ksh en la última línea del fragmento a continuación.

IFS=/
dir="${FOLDERS[*]}"
unset IFS
dir=${dir//\/+(\/)//}
Gilles 'SO- deja de ser malvado'
fuente
3

¿Cómo sedno te funciona? Intente sed 's|/\+|/|g'después de concatenar, o sed 's|/||g'antes.

Kevin
fuente
1

bash 4introdujo globbing extendido, que permite la coincidencia de expresiones regulares en la sustitución de parámetros ${var....}... Está desactivado por defecto. Para habilitarlo para su script, simplemente configure la opción de shell extglob ...

Suponiendo que $ dir ==/home/me/////////stuff//items

shopt -s extglob; dir="${dir//+(\/)//}"

Valor resultante de $ dir

/home/me/stuff/items  

Aquí hay algunos ejemplos con do-sy-don't-s - Bash Extended Globbing

Peter.O
fuente