The OpenNET Project / Index page

[ новости /+++ | форум | wiki | теги | ]



"Как передать несколько путей с пробелами через переменную?"
Вариант для распечатки  
Пред. тема | След. тема 
Форум Открытые системы на рабочей станции (Консоль)
Изначальное сообщение [ Отслеживать ]

"Как передать несколько путей с пробелами через переменную?"  +/
Сообщение от inFlowiaLab (ok), 28-Май-20, 19:11 
пусть есть неопределённое количество путей к файлам или каталогам которое я получаю например от zenity. В путях могут быть пробелы. Мне нужно все эти пути передать какой либо команде, принимающей несколько путей разделённых пробелами.
Передавать придётся через переменную, как мне в неё записать эти пути, чтобы они верно передались?
вариант c заворачиванием путей в одинарные кавычки и всего этого в двойные кавычки
p=" '/п 1/ф 1' '/п 2/ф 2' "
ls $p
не прокатывает.
Как ещё можно завернуть пути с пробелами?
Ответить | Правка | Cообщить модератору

Оглавление

Сообщения [Сортировка по времени | RSS]


1. "Как передать несколько путей с пробелами через переменную?"  +2 +/
Сообщение от Licha Morada (ok), 28-Май-20, 20:50 
> 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

Ответить | Правка | Наверх | Cообщить модератору

2. "Как передать несколько путей с пробелами через переменную?"  +/
Сообщение от DiabloPC (ok), 29-Май-20, 03:14 
> Понадеемся что у вас вас нет файлов с переводом строки в имени.

А такое вообще возможно?

Ответить | Правка | Наверх | Cообщить модератору

3. "Как передать несколько путей с пробелами через переменную?"  +1 +/
Сообщение от Licha Morada (ok), 29-Май-20, 03:42 
>> Понадеемся что у вас вас нет файлов с переводом строки в имени.
> А такое вообще возможно?

Увы.
Я не пробовал, но https://unix.stackexchange.com/questions/118959/how-to-find-...

Ответить | Правка | Наверх | Cообщить модератору

4. "Как передать несколько путей с пробелами через переменную?"  +/
Сообщение от DiabloPC (ok), 29-Май-20, 09:24 
>>> Понадеемся что у вас вас нет файлов с переводом строки в имени.
>> А такое вообще возможно?
> Увы.
> Я не пробовал, но https://unix.stackexchange.com/questions/118959/how-to-find-...

Н-да, вот в такие моменты понимаешь что на самом деле незнаешь нихрена %)

Ответить | Правка | Наверх | Cообщить модератору

5. "Как передать несколько путей с пробелами через переменную?"  +/
Сообщение от inFlowiaLab (ok), 29-Май-20, 09:45 
> А такое вообще возможно?

Возможно! Буквально неделю назад сделал ошибку в скрипте и он мне генерил каталог для лог-файлов с двухэтажным названием х) Файловый менеджер Caja даже корректно это всё отображал - каталог и под ним 2 строчки названия)

Ответить | Правка | К родителю #2 | Наверх | Cообщить модератору

6. "Как передать несколько путей с пробелами через переменную?"  +/
Сообщение от inFlowiaLab (ok), 29-Май-20, 22:23 
Спасибо большое. Остановился на таком варианте:

IFS='
'
path=`zenity --file-selection --directory --multiple --separator='
' --title="Выбери каталоги. (Переносы строки в именах файлов не поддерживаются!)"`
# ВНИМАНИЕ! Взятие $path в двойные кавычки недопустимо, из за IFS
do_some $path

Да, вот по непонятным мне причинам теперь если выполнять действие со взятием переменной с путями в кавычки то ничего не работает. Но это вроде бы не проблема. Можно ведь и не брать. Ещё из непонятного - это почему то не прокатывает указывать разделитель как '\n' а не как:
'
'
Вроде у zenity с этим проблемы, но не только у него. Я когда так делал без zenity и в конце выполнял ls $path у меня почему-то ls кроме указанных каталогов пытался ещё показать катлог под названием '' то есть пустая строка.
Ответить | Правка | К родителю #1 | Наверх | Cообщить модератору

7. "Как передать несколько путей с пробелами через переменную?"  +1 +/
Сообщение от Licha Morada (ok), 29-Май-20, 23:15 
> --title="Выбери каталоги. (Переносы строки в именах файлов не поддерживаются!)"

Прямо так с восклицательным знаком и работает? В Баше?

Чем дефолтный "|" в качестве сепаратора не устраивает? Гораздо более читабельно всё будет.

IFS имеет смысл задавать для конкетной команды, чтобы он не загрязнял дефолт для остального скрипта.

Не так:

 
IFS='|'
...
do_some $path

А так:

 
...
IFS='|' do_some $path

> указывать разделитель как '\n'

Так можно, но мудрить надо, навскидку не разберусь.

Ответить | Правка | Наверх | Cообщить модератору

8. "Как передать несколько путей с пробелами через переменную?"  +/
Сообщение от inFlowiaLab (ok), 30-Май-20, 09:26 
> Прямо так с восклицательным знаком и работает? В Баше?

Ну да. В первой строке скрипта: "#!/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"
:

Ответить | Правка | Наверх | Cообщить модератору

9. "Как передать несколько путей с пробелами через переменную?"  +/
Сообщение от Licha Morada (ok), 30-Май-20, 19:04 
>> Прямо так с восклицательным знаком и работает? В Баше?
> Ну да. В первой строке скрипта: "#!/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 останется на дефолте, а во втором изменится.

Работает в строчках которые запускают команды. В строчках которые присваивают значения, там странно.
Разумеется, делайте как вам будет удобнее.

Ответить | Правка | Наверх | Cообщить модератору

10. "Как передать несколько путей с пробелами через переменную?"  +/
Сообщение от DiabloPC (ok), 30-Май-20, 21:24 
А зачем внутри одного скрипта плодить несколько вариантов IFS (да и любых других переменных тоже)? После завершения работы скрипта оболочка всеравно вернётся к дефолту (ес-сно если не юзать export).
Ответить | Правка | Наверх | Cообщить модератору

13. "Как передать несколько путей с пробелами через переменную?"  +/
Сообщение от Licha Morada (ok), 01-Июн-20, 04:11 
> А зачем внутри одного скрипта плодить несколько вариантов IFS (да и любых
> других переменных тоже)? После завершения работы скрипта оболочка всеравно вернётся к
> дефолту (ес-сно если не юзать export).

Если скрипт небольшой, то пофигу.
А если большой, то имеет смысл стараться держать окружение максимально близким к дефолту, держа под контролем все отклонения, и возвращаясь к дефолту при первой возможности. Дабы меньше дебажить странные глюки.
Собственно, это не догма, а практическое соображение. Если оно облегчает жизнь, то надо следовать. Если нет, значит на то причина есть.

Ответить | Правка | Наверх | Cообщить модератору

14. "Как передать несколько путей с пробелами через переменную?"  +/
Сообщение от DiabloPC (ok), 01-Июн-20, 08:23 
>> А зачем внутри одного скрипта плодить несколько вариантов IFS (да и любых
>> других переменных тоже)? После завершения работы скрипта оболочка всеравно вернётся к
>> дефолту (ес-сно если не юзать export).
> Если скрипт небольшой, то пофигу.
> А если большой, то имеет смысл стараться держать окружение максимально близким к
> дефолту, держа под контролем все отклонения, и возвращаясь к дефолту при
> первой возможности. Дабы меньше дебажить странные глюки.
> Собственно, это не догма, а практическое соображение. Если оно облегчает жизнь, то
> надо следовать. Если нет, значит на то причина есть.

Имхо, в рамках одного скрипта проще такое делать для всего скрипта. Т.е установить переменную в самом начале и помнить что в этом скрипте IFS это перенос строки, а не пробел, а не сидеть потом через пару сотен строк и не офигевать почему оно вдруг работает не так как ожидалось, а оказывается мы где-то там забыли сбросить переменную до пробела. Оно вроди и мелочь, но на отладке такого потом глаза могут повылезать пока найдёшь %)

Ответить | Правка | Наверх | Cообщить модератору

20. "Как передать несколько путей с пробелами через переменную?"  +/
Сообщение от inFlowiaLab (ok), 01-Июн-20, 13:54 
> Работает в строчках которые запускают команды. В строчках которые присваивают значения,
> там странно.

Из за этой странности не изменять IFS глобально не получится. Провёл много проб и понял, что пока не сделаешь так:


IFS=$'\n' p='путь
путь
путь'
IFS=$'\n' ls $p

ничего не заработает. То есть в любом случае при задании переменной прихоидится указывать IFS=$'\n' а раз уж это всё равно глобально изменит IFS то смысла особого в этом нет.

Очень всё это странно, потому что если менять IFS на весь скрипт и не парится, то можно задавать переменную с путём даже до изменения IFS.

Ответить | Правка | К родителю #9 | Наверх | Cообщить модератору

21. "Как передать несколько путей с пробелами через переменную?"  +/
Сообщение от DiabloPC (ok), 01-Июн-20, 20:52 
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"

Т.е если это писать руками всё норм, а если делать подстановку - весь выхлоп пишется как один элемент массива, хотя выхлоп подстановки соответствует тому что пишется руками и должен быть воспринят как несколько элементов. Но нет, оно его тащит как строку и хоть ты тресни %)
Ответить | Правка | Наверх | Cообщить модератору

11. "Как передать несколько путей с пробелами через переменную?"  +/
Сообщение от DiabloPC (ok), 30-Май-20, 21:27 
> непонятного - это почему то не прокатывает указывать разделитель как '\n'

IFS=$'\n'

Ответить | Правка | К родителю #6 | Наверх | Cообщить модератору

12. "Как передать несколько путей с пробелами через переменную?"  +/
Сообщение от inFlowiaLab (ok), 31-Май-20, 18:22 
>> непонятного - это почему то не прокатывает указывать разделитель как '\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': Нет такого файла или каталога


Господи, откуда он символ доллара в пути взял...
Ответить | Правка | Наверх | Cообщить модератору

15. "Как передать несколько путей с пробелами через переменную?"  +1 +/
Сообщение от DiabloPC (ok), 01-Июн-20, 08:30 
Потому что не 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


Ответить | Правка | Наверх | Cообщить модератору

17. "Как передать несколько путей с пробелами через переменную?"  +/
Сообщение от inFlowiaLab (ok), 01-Июн-20, 12:26 
> Конструкция $'\n' это и есть перенос строки.

Всё! Вот чего я не понимал в контексте проблемы с IFS='\n'. Что \n заменяется на перенос строки только в одинарных кавычках и только если перед ними стоит $

Ответить | Правка | Наверх | Cообщить модератору

19. "Как передать несколько путей с пробелами через переменную?"  +/
Сообщение от DiabloPC (ok), 01-Июн-20, 12:37 
>> Конструкция $'\n' это и есть перенос строки.
> Всё! Вот чего я не понимал в контексте проблемы с IFS='\n'. Что
> \n заменяется на перенос строки только в одинарных кавычках и только
> если перед ними стоит $

Агась. А ниже вон человек самый правильный вариант для этого всего предложил. И IFS трогать ненужно и, по идее, там даже переносы строк не помеха.

Ответить | Правка | Наверх | Cообщить модератору

22. "Как передать несколько путей с пробелами через переменную?"  +/
Сообщение от And (??), 01-Июн-20, 21:14 
> И 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/bash

mkdir "/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/bash

PS4="+:$( 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, здесь ведь научился.

Ответить | Правка | Наверх | Cообщить модератору

16. "Как передать несколько путей с пробелами через переменную?"  +1 +/
Сообщение от And (??), 01-Июн-20, 12:07 

#!/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"

Ответить | Правка | Наверх | Cообщить модератору

18. "Как передать несколько путей с пробелами через переменную?"  +/
Сообщение от DiabloPC (ok), 01-Июн-20, 12:27 
Люто плюсую!
Массивы же зачем-то существуют))))
Ответить | Правка | Наверх | Cообщить модератору

Архив | Удалить

Рекомендовать для помещения в FAQ | Индекс форумов | Темы | Пред. тема | След. тема




Спонсоры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2023 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру