- Как передать несколько путей с пробелами через переменную?, Licha Morada, 20:50 , 28-Май-20 (1) +2
> p=" '/п 1/ф 1' '/п 2/ф 2' " > ls $p > не прокатывает. > Как ещё можно завернуть пути с пробелами?Пусть пробел будет разделителем слов, а перевод строки разделителем путей. Понадеемся что у вас вас нет файлов с переводом строки в имени. Пути храним в переменной, по одному передаём их команде. IFS='' p="/п 1/ф 1 /п 2/ф 2 /п 3/ф 3" echo $p | while read FILEPATH; do ls "$FILEPATH"; done (Итерировать, наверное, можно как-то покрасивше.) Пусть пробел будет разделителем слов, а символ ° разделителем путей. Понадеемся что у вас вас нет файлов с переводом "°" в имени. Пути храним в переменной, передаём их команде все вместе.
IFS='°' p="/п 1/ф 1°/п 2/ф 2°/п 3/ф 3" IFS='°' ls $p Что такое IFS: https://bash.cyberciti.biz/guide/$IFS https://unix.stackexchange.com/questions/16192/what-is-the-ifs
- Как передать несколько путей с пробелами через переменную?, DiabloPC, 03:14 , 29-Май-20 (2)
> Понадеемся что у вас вас нет файлов с переводом строки в имени.А такое вообще возможно?
- Как передать несколько путей с пробелами через переменную?, inFlowiaLab, 22:23 , 29-Май-20 (6)
Спасибо большое. Остановился на таком варианте:
IFS=' ' path=`zenity --file-selection --directory --multiple --separator=' ' --title="Выбери каталоги. (Переносы строки в именах файлов не поддерживаются!)"` # ВНИМАНИЕ! Взятие $path в двойные кавычки недопустимо, из за IFS do_some $path
Да, вот по непонятным мне причинам теперь если выполнять действие со взятием переменной с путями в кавычки то ничего не работает. Но это вроде бы не проблема. Можно ведь и не брать. Ещё из непонятного - это почему то не прокатывает указывать разделитель как '\n' а не как: ' ' Вроде у zenity с этим проблемы, но не только у него. Я когда так делал без zenity и в конце выполнял ls $path у меня почему-то ls кроме указанных каталогов пытался ещё показать катлог под названием '' то есть пустая строка.
- Как передать несколько путей с пробелами через переменную?, Licha Morada, 23:15 , 29-Май-20 (7) +1
> --title="Выбери каталоги. (Переносы строки в именах файлов не поддерживаются!)"Прямо так с восклицательным знаком и работает? В Баше? Чем дефолтный "|" в качестве сепаратора не устраивает? Гораздо более читабельно всё будет. IFS имеет смысл задавать для конкетной команды, чтобы он не загрязнял дефолт для остального скрипта. Не так:
IFS='|' ... do_some $path
А так:
... IFS='|' do_some $path
> указывать разделитель как '\n' Так можно, но мудрить надо, навскидку не разберусь.
- Как передать несколько путей с пробелами через переменную?, inFlowiaLab, 09:26 , 30-Май-20 (8)
> Прямо так с восклицательным знаком и работает? В Баше?Ну да. В первой строке скрипта: "#!/bin/bash", запускается на Ubuntu Studio 19.10 в xfce4-terminal 0.8.8. А где такое не должно работать? > Чем дефолтный "|" в качестве сепаратора не устраивает? Гораздо более читабельно всё > будет. Читабельнее-то будет, но я тут недавно узанал... Вернее не смог узнать существует ли в принципе в природе символ который ext4 не позволил бы мне засунуть в имя, так что я был не хило озадачен вопросом, а что мне вообще тогда в качестве разделителя использовать. Пришёл к выводу, что перенос строки - самый лучший вариант, так как самому мне в голову не придёт его в имени файла юзать, да и никогда не встречал чтобы кто-то другой его туда вставлял, к тому же двухэтажные имена легко замечаются в ФМ и я их в случае чего легко выловлю и почищу от переносов. > IFS имеет смысл задавать для конкетной команды, чтобы он не загрязнял дефолт > для остального скрипта. Знаете, сперва бросился исправлять, а пртом протестил и пришёл к выводу что такая форма записи тоже загрязняет дефолт, либо я чего-то не понимаю. Вот проведённый эксперимент:
i@MediaLab1:~$ IFS='|' i@MediaLab1:~$ echo "$IFS" | i@MediaLab1:~$ IFS=':' p='dd' i@MediaLab1:~$ echo "$IFS" :
- Как передать несколько путей с пробелами через переменную?, Licha Morada, 19:04 , 30-Май-20 (9)
>> Прямо так с восклицательным знаком и работает? В Баше? > Ну да. В первой строке скрипта: "#!/bin/bash", запускается на Ubuntu Studio 19.10 > в xfce4-terminal 0.8.8. > А где такое не должно работать?Баш интерпретирует восклицательный знак в последовательностях и его обычно приходится экранировать. https://www.itworld.com/article/2717119/linux-tip--using-an-... >[оверквотинг удален] > такая форма записи тоже загрязняет дефолт, либо я чего-то не понимаю. > Вот проведённый эксперимент: > > i@MediaLab1:~$ IFS='|' > i@MediaLab1:~$ echo "$IFS" > | > i@MediaLab1:~$ IFS=':' p='dd' > i@MediaLab1:~$ echo "$IFS" > : >
Сравните:
LANG=C date echo $LANG
С
LANG=C date echo $LANG
Вывод date будет один и тот-же, на языке соответствущем LANG. В первом случае, после исполнения date, значение LANG останется на дефолте, а во втором изменится. Работает в строчках которые запускают команды. В строчках которые присваивают значения, там странно. Разумеется, делайте как вам будет удобнее.
- Как передать несколько путей с пробелами через переменную?, DiabloPC, 21:24 , 30-Май-20 (10)
А зачем внутри одного скрипта плодить несколько вариантов IFS (да и любых других переменных тоже)? После завершения работы скрипта оболочка всеравно вернётся к дефолту (ес-сно если не юзать export).
- Как передать несколько путей с пробелами через переменную?, Licha Morada, 04:11 , 01-Июн-20 (13)
> А зачем внутри одного скрипта плодить несколько вариантов IFS (да и любых > других переменных тоже)? После завершения работы скрипта оболочка всеравно вернётся к > дефолту (ес-сно если не юзать export).Если скрипт небольшой, то пофигу. А если большой, то имеет смысл стараться держать окружение максимально близким к дефолту, держа под контролем все отклонения, и возвращаясь к дефолту при первой возможности. Дабы меньше дебажить странные глюки. Собственно, это не догма, а практическое соображение. Если оно облегчает жизнь, то надо следовать. Если нет, значит на то причина есть.
- Как передать несколько путей с пробелами через переменную?, DiabloPC, 08:23 , 01-Июн-20 (14)
>> А зачем внутри одного скрипта плодить несколько вариантов IFS (да и любых >> других переменных тоже)? После завершения работы скрипта оболочка всеравно вернётся к >> дефолту (ес-сно если не юзать export). > Если скрипт небольшой, то пофигу. > А если большой, то имеет смысл стараться держать окружение максимально близким к > дефолту, держа под контролем все отклонения, и возвращаясь к дефолту при > первой возможности. Дабы меньше дебажить странные глюки. > Собственно, это не догма, а практическое соображение. Если оно облегчает жизнь, то > надо следовать. Если нет, значит на то причина есть.Имхо, в рамках одного скрипта проще такое делать для всего скрипта. Т.е установить переменную в самом начале и помнить что в этом скрипте IFS это перенос строки, а не пробел, а не сидеть потом через пару сотен строк и не офигевать почему оно вдруг работает не так как ожидалось, а оказывается мы где-то там забыли сбросить переменную до пробела. Оно вроди и мелочь, но на отладке такого потом глаза могут повылезать пока найдёшь %)
- Как передать несколько путей с пробелами через переменную?, inFlowiaLab, 13:54 , 01-Июн-20 (20)
> Работает в строчках которые запускают команды. В строчках которые присваивают значения, > там странно.Из за этой странности не изменять IFS глобально не получится. Провёл много проб и понял, что пока не сделаешь так:
IFS=$'\n' p='путь путь путь' IFS=$'\n' ls $p
ничего не заработает. То есть в любом случае при задании переменной прихоидится указывать IFS=$'\n' а раз уж это всё равно глобально изменит IFS то смысла особого в этом нет. Очень всё это странно, потому что если менять IFS на весь скрипт и не парится, то можно задавать переменную с путём даже до изменения IFS.
- Как передать несколько путей с пробелами через переменную?, DiabloPC, 20:52 , 01-Июн-20 (21)
Ta там странностей немерено: Тот вариант с массивами, что человек ниже предложил: Если делать вот так
declare -a array=("c 1" "c 2") To всё работает и соответственно если сделать
for i in ${array[@]}; do echo $i done На выхлопе получим
c 1 c 2 Но если сделать
declare -a array=("$(zenity --file-selection --multiple --separator='" "')") To в массив попадёт только один вот такой элемент:
"c 1" "c 2" Т.е если это писать руками всё норм, а если делать подстановку - весь выхлоп пишется как один элемент массива, хотя выхлоп подстановки соответствует тому что пишется руками и должен быть воспринят как несколько элементов. Но нет, оно его тащит как строку и хоть ты тресни %)
- Как передать несколько путей с пробелами через переменную?, DiabloPC, 21:27 , 30-Май-20 (11)
> непонятного - это почему то не прокатывает указывать разделитель как '\n' IFS=$'\n'
- Как передать несколько путей с пробелами через переменную?, inFlowiaLab, 18:22 , 31-Май-20 (12)
>> непонятного - это почему то не прокатывает указывать разделитель как '\n' > IFS=$'\n' Решил проверить, начав с самого рабочего варианта и теперь такое впечатление что теперь всё стало работать совсем не так. Хоть убей не вижу своей ошибки:
i@MediaLab1:/media/i/Tmp/ScriptTestingPOLYGON$ IFS=' > 'i@MediaLab1:/media/i/Tmp/ScriptTestingPOLYGON$ ls 'c 1 > c 2' ls: невозможно получить доступ к 'c 1'$'\n''c 2': Нет такого файла или каталога
Господи, откуда он символ доллара в пути взял...
- Как передать несколько путей с пробелами через переменную?, DiabloPC, 08:30 , 01-Июн-20 (15) +1
Потому что не ls 'c 1 c 2', a ls "c 1" "c 2" или 'c 1' 'c 2'. Конструкция $'\n' это и есть перенос строки. В варианте 'c 1 c 2' оно какраз пытается найти двухэтажное имя. т.е:
'c 1 c 2' == 'c 1'$'\n''c 2'a 'c 1' 'c 2' == '"c 1" "c 2"' Т.е в кавычки либо не брать вообще, либо имя каждого каталога отдельно. [ diablopc@d200 ~/test ]$ cat test.sh #!/bin/bash IFS=$'\n' SELECTION=$(zenity --file-selection --directory --multiple --separator="$IFS" --title="olololo") ls $SELECTION [ diablopc@d200 ~/test ]$ ./test.sh '/home/diablopc/test/c 1': 1_1 1_2
'/home/diablopc/test/c 2': 2_1 2_2
- Как передать несколько путей с пробелами через переменную?, inFlowiaLab, 12:26 , 01-Июн-20 (17)
> Конструкция $'\n' это и есть перенос строки.Всё! Вот чего я не понимал в контексте проблемы с IFS='\n'. Что \n заменяется на перенос строки только в одинарных кавычках и только если перед ними стоит $
- Как передать несколько путей с пробелами через переменную?, DiabloPC, 12:37 , 01-Июн-20 (19)
>> Конструкция $'\n' это и есть перенос строки. > Всё! Вот чего я не понимал в контексте проблемы с IFS='\n'. Что > \n заменяется на перенос строки только в одинарных кавычках и только > если перед ними стоит $ Агась. А ниже вон человек самый правильный вариант для этого всего предложил. И IFS трогать ненужно и, по идее, там даже переносы строк не помеха.
- Как передать несколько путей с пробелами через переменную?, And, 21:14 , 01-Июн-20 (22)
> И IFS трогать ненужно и, по идее, там даже переносы строк > не помеха.Да. Кавычки расставлены так, что при разворачивании всё оказывается целыми строками, независимо от наличия IFS или других специальных символов внутри значений. Различие между @ и * при развёртке массива. Не трогая IFS можно вот так (Zenity заменена на find, для примера): while read filename ; do echo "I found name: '${filename}'" done <<< "$( find /tmp -maxdepth 1 )"
В зависимости от реализации сам(и) IFS можно тронуть, если явно удобно. Если каждая строка это реально отдельный элемент массива (что не всегда), то можно вот так, с бэкапом IFS, если нужен: Синтаксис описан здесь: https://wiki.bash-hackers.org/syntax/arrays #!/bin/bashmkdir "/tmp/asd fgh" "/tmp/qwe rty" IFS_back="${IFS}" IFS=$'\n' declare -a strings_array=($( ls -1 /tmp )) for single_string in "${strings_array[@]}" ; do if [[ "${single_string}" =~ ^"asd".*|.*" rty"$ ]] ; then # Прячу прочее содержимое своего /tmp, показываю только нужные имена. echo $single_string fi done IFS="${IFS_back}"
В зависимости от идеи бывает нужно сохранить для обработки позже. Причём IFS не трогается. Но больше вычислений - медленнее. Можно так:
declare -a str_array=() while read filename ; do str_array+=("${filename}") done <<< "$( find /tmp -maxdepth 1 )" echo "${str_array[0]}" echo "${str_array[1]}" echo "Total found: ${#str_array[@]}"
Либо ещё сильнее: declare -A str_map=() while read path ; do str_map[$path]=$( basename "${path}" ) done <<< "$( find /tmp -maxdepth 1 )"for dir_name in "${!str_map[@]}" ; do echo "Have file '${str_map[$dir_name]}' in '$(dirname "${dir_name}")'" done echo "Total found: ${#str_map[@]}"
Иногда по логике полезно применять логгирование и свал скрипта на первом возврате ошибки (рекомендации от Debian), объявления переменных readonly. Это позволяет гораздо меньше морочиться обработкой ошибок и быстрее их находить. #!/bin/bash PS4="+:$( basname \"\${0}\" ):\${LINENO}: " set -xeu -o pipefail declare -r some_name="asdfgh"
Применяется всё это примерно вот так: #!/bin/bashPS4="+:$( basename \"\${0}\" ):\${LINENO}: " set -xeu -o pipefail function do_did_done { local -A str_map while read path ; do str_map[$path]=$( basename "${path}" ) done <<< "$( find /tmp -maxdepth 1 )" total_found="${#str_map[@]}" } total_found=0 do_did_done set +e # Выключил свал. false # Сгенерировал код возврата ошибка, а оно не падает. echo "ИНФОРМАЦИЯ:${0}:${LINENO}: Total found ${total_found}" # Не залоггировало саму команду т.к. set +x set -e # Включил свал обратно. Осторожно, при определённых условиях внутрь функции не наследуется. set -x # Чтобы показало, на чём оно упадёт declare -r dir_name="никогда-не-было-такой-директории" test -d "${dir_name}" # А вот тут упало, с очень показательной диагностикой, т.к. set -e !!! echo "Сюда уже не дойдёт."
По причине '<<<' - это Bash код. По причине "declare -A" - это версия Bash 4+ До встречи в продакшн!! :) P.S. Отвечая на старинный вопрос: зачем Вы используете эти башизмы '<<<'? Т.к. при таком приёме переменная, объявленная внутри цикла, сохранится по окончании цикла. Спасибо Opennet, здесь ведь научился.
- Как передать несколько путей с пробелами через переменную?, And, 12:07 , 01-Июн-20 (16) +1
#!/bin/bash declare -a strings_array=("asd fgh" "qwe rty") for single_string in "${strings_array[@]}" ; do echo "$single_string" done
#!/bin/bash declare -a strings_array=("asd fgh" "qwe rty") cd /tmp mkdir "${strings_array[@]}" ls -ld "/tmp/asd fgh" ls -ld "/tmp/qwe rty"
|