Прочно строим дорожки

16

Скажем, у меня есть несколько переменных в сценарии оболочки (например, в zsh):

FOLDER_1, FOLDER_2, etc.

Эти переменные относятся к папкам по убыванию /. Например, если у меня есть путь/home/me/stuff/items

переменные будут:

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

Теперь скажите, что я хочу построить соответствующий путь путем объединения переменных. Один из возможных способов - построить путь следующим образом:

PATH=$FOLDER_1/$FOLDER_2/$FOLDER_3/

Тем не менее, скажем, что некоторые переменные FOLDER_iидут с косой чертой, в то время как другие не (и мы не знаем, какие), например

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

Мой вопрос: как я мог построить путь надежно? (например, избегая двойных слешей и добавляя их там, где они должны быть).

Я подумал, что один из способов сделать это - добавить /всегда между парами переменных, а затем удалить любые дубликаты с помощью sed, но я не могу заставить его работать (я не уверен, что я /правильно обрабатываю sed).

Кроме того, я изобретаю колесо ? (т. е. есть ли встроенный, который это уже делает?).

Наконец, если переменные находятся в массиве , например FOLDERS, возможно ли сделать это без зацикливания? (или, альтернативно, циклично, но не зная, сколько FOLDERSих в массиве).

Амелио Васкес-Рейна
источник

Ответы:

12

Вы можете использовать printfс массивом:

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

В %этом случае оператор обрезает завершающие строки /. Применяя его parts[@], он обрезает каждый элемент массива отдельно.

Ключ к пониманию этого printfтрюка заключается в следующем man 1 printf: «Строка формата используется повторно так часто, как это необходимо для удовлетворения аргументов».

janmoesen
источник
Одиночный %метод работает для одиночной косой черты, упомянутой в квесте. Для обслуживания, когда есть несколько слэшей, ${parts[@]%%/*}работает. Вот ссылка на немного больше информации о проблеме косой черты: Что означают двойные косые черты в пути UNIX? Является ли 'cd dir / subdir // действует ...
Peter.O
2
@fered: /*в данном случае это не ноль или более косых черт , а скорее косая черта, за которой следует любое количество символов. Это означает, что если ваш путь начинается с косой черты, результатом будет пустая строка!
10
Согласно примеру, я считал, что будут только косые черты. Тем не менее, чтобы приспособиться к возможности использования косой черты, можно использовать bash extglob(с регулярным выражением) .. shopt -s extglob; ${parts[@]%%/+(/)}...
Peter.O 26.10.11
15

Простой ответ - перестать беспокоиться и любить множественные слэши. Несколько косых черт имеют тот же эффект, что и одна косая черта (за исключением того, что путь, начинающийся с, //имеет различное значение в нескольких системах; я могу назвать только слои эмуляции unix в Windows). Именно так и было задумано, и возможность собирать имена файлов, не беспокоясь о множественных слешах, была основной частью этого проектного решения.

Чтобы объединить элементы массива, в zsh вы можете использовать j флаг расширения параметра .

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

Вы можете раздавить дублирующую косую черту, пока вы на ней, но это чисто косметическое средство.

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

В ksh и bash вы можете объединить массив, используя первый символ $IFSв качестве разделителя. Затем вы можете сжать дубликаты. В bash вам нужно будет shopt -s extglobвключить глобусы ksh в последней строке фрагмента ниже.

IFS=/
dir="${FOLDERS[*]}"
unset IFS
dir=${dir//\/+(\/)//}
Жиль "ТАК - перестань быть злым"
источник
3

Как sedу вас не работает? Попробуйте sed 's|/\+|/|g'после объединения или sed 's|/||g'до.

Kevin
источник
1

bash 4введено расширенное глобирование, которое позволяет сопоставлять регулярные выражения при подстановке параметров ${var....}... По умолчанию оно отключено. Чтобы включить его для вашего скрипта, просто установите параметр оболочки extglob ...

Предполагая, что $ dir ==/home/me/////////stuff//items

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

Результирующее значение $ dir

/home/me/stuff/items  

Вот несколько примеров с do-s и not-s - расширенная глобализация Bash

Peter.O
источник