LinuxForum.Fans
Тема

Обработка пробелов в именах файлов

Автор: Cruiser78 29 сообщений 9104 просмотров
#316124

Привет всем!

Коллеги, помогите решить следующую заморочку в скриптинге. А именно:
1. Есть наутилус, который может исполнять различные скрипты из контекстного меню.
2. Настраиваю простенкий скриптик для показа слайдшоу с помощью программы feh.

text
#!/bin/sh
FILE_PATHS=`echo $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS | sed 's/ /\\\ /g'` | sed 's/\\\ \// \//g'`
#FILE_PATHS=$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS
DELAY=1
logger -t slideshow  "feh --title Slideshow -rFx -S name -D $DELAY  $FILE_PATHS"
feh --title "Slideshow" -rFx -S name -D $DELAY $FILE_PATHS

Суть скрипта проста - вызвать feh и передать ему параметрами или название каталога, в котором картинки лежат, или разделенный пробелами перечень полных имен этих самых картинок ($NAUTILUS_SCRIPT_SELECTED_FILE_PATHS). Если в этих именах нет пробелов, то всё работает без проблемно. Но как только в имени появляется пробел - feh на этом деле ожидаемо спотыкается. Фигня вопрос - экранируем пробел, то есть вместо пробела " " ставим "\ " и возвращаем "пробел" между именами файлов в первобытный вид. Это делает первая строка скрипта. Из строки feh --title Slideshow -rFx -S name -D 1 /home/alex/Изображения/FujiFilm/Зи ма/DSCF0005.JPG /home/alex/Изображения/FujiFilm/Зи ма/DSCF0006.JPG /home/alex/Изображения/FujiFilm/Зи ма/DSCF0007.jpg получаем строку feh --title Slideshow -rFx -S name -D 1 /home/alex/Изображения/FujiFilm/Зи\ ма/DSCF0005.JPG /home/alex/Изображения/FujiFilm/Зи\ ма/DSCF0006.JPG /home/alex/Изображения/FujiFilm/Зи\ ма/DSCF0007.jpg. Преобразованная строка, для контроля, пишется логгером в /val/log/messages.
Если эту преобразованную строку вызвать на исполнение прямо из терминала или по ALT+F2, из командной строки, - feh отрабатывает корректно, прекрасно понимает экранированные пробелы и показывает нужное слайдшоу. А вот если вызывать тот же feh из данного скрипта, то абсолютно ничего не происходит. Не хочет он отрабатывать в этом случае пробелы в именах.

Коллеги, подскажите, что я не так делаю?

#316129

Попробуйте в последней строке скрипта переменную $FILE_PATHS взять в двойные кавычки.

#316131
Lupo Alberto написал:
Попробуйте в последней строке скрипта переменную $FILE_PATHS взять в двойные кавычки.

Пробовалось. Более того, пробовалось даже каждое имя файла закавычивать. Не выходит каменный цветок. :(

#316133
text

#!/bin/bash
OIFS=$IFS
IFS="
"
DELAY=1
feh --title "Slideshow" -rFx -S name -D $DELAY $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS
IFS=$OIFS
#316142

или так:

text
echo "$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS" | xargs feh --title Slideshow -rFx -S name -D $DELAY
#316153

Доброго времени!
Если Вы используете именно такой скрипт, то ошибка очевидна.
Обратите внимание на следующую строку:

text
FILE_PATHS=`echo $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS | sed 's/ /\\\ /g'` | sed 's/\\\ \// \//g'`

используется три back quote/backtick - `
>абсолютно ничего не происходит
Выполняем в терминале эмулятора:

text
$ nautilus -q
text
$ nautilus

теперь ошибки скрипта будут отображаться в эмуляторе терминала.
Вероятно, Вы увидите следующее сообщение:
EOF in backquote substitution
Что соответствует вышеописанной ошибке (в частности).

Также, вместо $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS Вы могли бы использовать стандартную перемененную $@.
С её использованием, скрипт приобретает следующий вид:

text
#!/bin/sh
DELAY=1
logger -t slideshow  "feh --title Slideshow -rFx -S name -D $DELAY  $@"
feh --title "Slideshow" -rFx -S name -D $DELAY "$@"

Удачи!

#316168

Как все усложнили))

на много проще выполнить так

text
#!/bin/bash

DELAY=1 feh --title "Slideshow" -rFx -S name -D $DELAY ./

Зы: не нравится только что используется рекурсивный поиск по каталогам
лучше бы заменить параметр -rFx на -Fx

#316174
"27874⇓" написал:
Вы могли бы использовать стандартную перемененную $@.

Эта переменная возвращает имя файла в текущем каталоге, а не самого каталога.
Работать врят ли будет когда наводишь на конкретный файл, сам скрипт работает только по тому, что когда курсор не стоит на файле, переменная возвращает пустое значение, что соответствует команде:

text
feh --title "Slideshow" -rFx -S name -D $DELAY

без задания каталога, feh по умолчанию использует текущий каталог.
А так самое простое, для получения пути каталога можно использовать команду pwd, с переменной гнома $NAUTILUS_SCRIPT_CURRENT_URI возвращающей путь каталога слишком много заморочек, проще использовать pwd

#316180
27874 написал:

теперь ошибки скрипта будут отображаться в эмуляторе терминала.
Вероятно, Вы увидите следующее сообщение:
EOF in backquote substitution
Что соответствует вышеописанной ошибке (в частности).

Нет, ошибка, точнее предупреждение, иная:

[console]feh WARNING: /home/alex/Изображения/FujiFilm/Зи\ ма does not exist - skipping
feh: No loadable images specified.
[/console]
Это если наравить скрипт на выделенный каталог "зи ма". Если же там собрать несколько картинок, то прочитаем следующее:
[console]feh WARNING: /home/alex/Изображения/FujiFilm/Зи\ ма/DSCF0005.JPG /home/alex/Изображения/FujiFilm/Зи\ ма/DSCF0006.JPG /home/alex/Изображения/FujiFilm/Зи\ ма/DSCF0007.jpg does not exist - skipping
feh: No loadable images specified.
[/console]
При этом, если эти же самые пути, имена отдать feh'у непосредственно из командной строки, то он проиграет нужное слайдшоу.
Ну и если в именах нет пробелов, то проиграет всё. Независимо от способа вызова.

#316183
27874 написал:

Также, вместо $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS Вы могли бы использовать стандартную перемененную $@.

Да дело не в имени переменной. Конечно можно использовать и $@. Но ведь и эта переменная содержать будет в себе пробелы в именах файлов. И соответсвенно feh будет интерпетировать эти пробелы как разделители между аргументами. То есть, получать будем что-то вроде этого:
[console]feh WARNING: /home/alex/Изображения/FujiFilm/Зи does not exist - skipping
feh WARNING: ма does not exist - skipping
feh: No loadable images specified.
See 'man feh' for detailed usage information
feh WARNING: Зи does not exist - skipping
feh WARNING: ма does not exist - skipping
feh: No loadable images specified.
[/console]
Что вполне ожидаемо и, естественно, неправильно. Правильно - заэкранировать пробел. Но при этом, если идет вызов феха из скрипта - он не видит файла. А если из командной строки - всё намано. Вот в чем проблема. Почему это так - понять не могу.
Да, и совершенно естественно, если запускать feh без выделения каталога-файлов, то есть с пустым перечнем, то он нормально покажет всё, что лежит в текущем каталоге.

#316189

Cruiser78,
так вариант с IFS у вас работает-то? Ведь и обрабатывать пробелы никак не надо, всё само.

#316192

Доброго времени!
Для ormorph:
У меня не стоит задача «создать скрипт для такой-то цели», я смотрю на исходный код и лишь отвечаю на вопросы, делаю уместные (на мой взгляд) замечания и пояснения.
>Эта переменная возвращает имя файла в текущем каталоге, а не самого каталога.
Не отрицаю, совершенно верно, $@ не содержит полного пути, но в данном скрипте, указание полного пути не требуется.
Мне следовало, выразить свою мысль более точно:
Вы могли бы использовать, в данном случае, стандартную перемененную $@.
>Работать врят ли будет когда наводишь на конкретный файл
Попрошу какие-нибудь доводы, или обоснования данного утверждения. Вы (насколько мне известно), в данном утверждении не правы.

Поддержу Ваш поиск неточностей.
>Как все усложнили))
>на много проще выполнить так
Извините, но Ваш упрощённый подход помимо реализации, также сокращает функциональность первоначального скрипта.
В данной реализации, невозможно определить перечень файлов для показа - будут отображены все файлы директории.
>Работать врят ли будет когда наводишь на конкретный файл

Для Cruiser78:
Пожалуйста, читайте дословно и отвечайте развёрнуто.
1. Я написал, если Вы используете скрипт из первого поста, то там имеется некорректное использование backquote, и если Вы выполните его, то будет отображено сообщение об ошибке следующего содержания:
«EOF in backquote substitution»
2.
>Нет, ошибка, точнее предупреждение, иная
Значит, Вы используете сейчас скрипт не из первого сообщения.
3.
>Да дело не в имени переменной. Конечно можно использовать и $@. Но ведь и эта переменная содержать будет в себе пробелы в именах файлов. И соответсвенно feh будет интерпетировать эти пробелы как разделители между аргументами. То есть, получать будем что-то вроде этого:
Пожалуйста, ознакомьтесь с тем, чем является переменная $@.
Из руководства bash:

@

Expands to the positional parameters, starting from one. When the expansion occurs within double quotes, each parameter expands to a separate word. That is, "$@" is equivalent to "$1" "$2" …. If the double-quoted expansion occurs within a word, the expansion of the first parameter is joined with the beginning part of the original word, and the expansion of the last parameter is joined with the last part of the original word. When there are no positional parameters, "$@" and $@ expand to nothing (i.e., they are removed).

4.
>Если эту преобразованную строку вызвать на исполнение прямо из терминала или по ALT+F2, из командной строки, - feh отрабатывает корректно, прекрасно понимает экранированные пробелы и показывает нужное слайдшоу.
Нам требуется лишь воспользоваться eval.

text
#!/bin/sh
FILE_PATHS=`echo $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS | sed 's/ /\\\ /g' | sed 's/\\\ \// \//g'`
#FILE_PATHS=$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS
DELAY=1
logger -t slideshow  "feh --title Slideshow -rFx -S name -D $DELAY  $FILE_PATHS"
eval feh --title "Slideshow" -rFx -S name -D $DELAY $FILE_PATHS

или более корректно

text
eval "feh --title \"Slideshow\" -rFx -S name -D $DELAY $FILE_PATHS"

Для bormant:
Спасибо!
Он должен работать
Я лично, лишь хотел показать автору, в чём проблема (пока не выходит, не понимают меня).

#316193
bormant написал:
Cruiser78,
так вариант с IFS у вас работает-то? Ведь и обрабатывать пробелы никак не надо, всё само.

Вариант с IFS - работает. Но мне не хватает на сей момент квалификации понять - почему.
To 27874:
1. Я показал именно тот скрипт, с которым отлаживаю процесс. Не надо думать, что я показываю одно, а работаю с другим.
2. Вашего сообщения об ошибке я не видел. Видел только те сообщения, которые опубликовал здесь. И суть которых мне непонятна. А именно feh при явном наличии вайлов-каталогов утверждает что их нет. Если идет запуск из скрипта. Если же сформированную в скрипте команду запустить из командной строки - проблемы нет, feh видит всё.
3. По реализации (целевой функции скрипта): основная задача - это обработать или выбранный каталог (заходя или не заходя в него), или выбранные каталоги (то есть несколько), или выбранные файлы из каталога. Под термином "обработать" следует понимать всё что угодно, например - распечатать заданное. feh это только моделька, показывающая работоспособность самого ланчера.

Устойчиво заработал следующий скрипт:

text

#/bin/sh
OIFS=$IFS
IFS="
"
DELAY=1
feh --title "Slideshow" -rFx -S name -D $DELAY $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS
IFS=$OIFS
#316204

Cruiser78,
так и спросите, расскажу.
1) Для начала почитайте man bash на предмет IFS. Это набор символов, которые являются разделителями, по умолчанию это пробел, табуляция, конец строки. Именно по ним bash разделяет параметры командной строки, чтобы затем передать их запускаемому процессу (да-да, тот самый второй параметр char **argv в функции main()).
2) Затем смотрим документацию по Nautilus-у в части $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS, увидим там упоминание того, что файлы в списке разделены символом LF (Line Feed -- перевод строки, \n).
3) Складываем (1) и (2), устанавливаем в качестве разделителя только перевод строки:
IFS="
"
после этого bash при запуске при формировании параметров поделит их по разрыву строки -- именно то, что и было нужно.

#316210
"27874⇓" написал:
(пока не выходит, не понимают меня).

Ну да)

#316215

bormant
Вроде как я понимаю не обязательно было возвращать IFS=$OIFS, вроде после ни каких команд после не идет. По завершению оболочки sh эти параметры сбрасываются сами.

#316220

Самый лаконичный в данном случае вариант предложил bormant, это я никаким образом не опровергаю, а напротив.
Я лишь объяснил, что я не ищу способ реализации, лишь корректирую приведённый исходный код, $@ добавил для кругозора.
Спорить или убеждать какого-то я не намерен. Я лишь говорю то, что считаю верным, привожу факты подкрепляющие мою позицию. Судя по всему, я зря потратил время, и Вы не стали вникать в суть, дело Ваше.
Спасибо за ответы!

text
$ nautilus --version

GNOME nautilus 3.4.2
text
$ feh --version

feh version 2.3
text
~/ifs$ ls -R

.:
0 img.jpg 1 img.jpg 2 img.jpg sub dir

./sub dir:
file 1.jpg file 2.jpg file 3.jpg

Ваш оригинальный скрипт, из первого сообщения.

text
#!/bin/sh
FILE_PATHS=`echo $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS | sed 's/ /\\\ /g'` | sed 's/\\\ \// \//g'`
#FILE_PATHS=$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS
DELAY=1
logger -t slideshow  "feh --title Slideshow -rFx -S name -D $DELAY  $FILE_PATHS"
feh --title "Slideshow" -rFx -S name -D $DELAY $FILE_PATHS

*.sh: 7: /home/user/.gnome2/nautilus-scripts/*.sh: Syntax error: EOF in backquote substitution

Убираю лишний backquote

text
#!/bin/sh
FILE_PATHS=`echo $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS | sed 's/ /\\\ /g' | sed 's/\\\ \// \//g'`
#FILE_PATHS=$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS
DELAY=1
logger -t slideshow  "feh --title Slideshow -rFx -S name -D $DELAY  $FILE_PATHS"
feh --title "Slideshow" -rFx -S name -D $DELAY $FILE_PATHS

feh WARNING: /home/user/ifs/sub\ does not exist - skipping
feh WARNING: dir does not exist - skipping
feh WARNING: /home/user/ifs/0\ does not exist - skipping
feh WARNING: img.jpg does not exist - skipping
feh WARNING: /home/user/ifs/1\ does not exist - skipping
feh WARNING: img.jpg does not exist - skipping
feh WARNING: /home/user/ifs/2\ does not exist - skipping
feh WARNING: img.jpg does not exist - skipping
feh: No loadable images specified.
See 'man feh' for detailed usage information
text
#!/bin/sh
FILE_PATHS=`echo $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS | sed 's/ /\\\ /g' | sed 's/\\\ \// \//g'`
#FILE_PATHS=$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS
DELAY=1
logger -t slideshow  "feh --title Slideshow -rFx -S name -D $DELAY  $FILE_PATHS"
eval feh --title "Slideshow" -rFx -S name -D $DELAY $FILE_PATHS

Нет сообщений об ошибках (работает), дабы продемонстрировать использую set -x

text
#!/bin/sh
set -x
FILE_PATHS=`echo $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS | sed 's/ /\\\ /g' | sed 's/\\\ \// \//g'`
#FILE_PATHS=$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS
DELAY=1
logger -t slideshow  "feh --title Slideshow -rFx -S name -D $DELAY  $FILE_PATHS"
eval feh --title "Slideshow" -rFx -S name -D $DELAY $FILE_PATHS

+ sed s/\\ \// \//g
+ sed s/ /\\ /g
+ echo /home/user/ifs/sub dir /home/user/ifs/0 img.jpg /home/user/ifs/1 img.jpg /home/user/ifs/2 img.jpg
+ FILE_PATHS=/home/user/ifs/sub\ dir /home/user/ifs/0\ img.jpg /home/user/ifs/1\ img.jpg /home/user/ifs/2\ img.jpg
+ DELAY=1
+ logger -t slideshow feh --title Slideshow -rFx -S name -D 1 /home/user/ifs/sub\ dir /home/user/ifs/0\ img.jpg /home/user/ifs/1\ img.jpg /home/user/ifs/2\ img.jpg
+ eval feh --title Slideshow -rFx -S name -D 1 /home/user/ifs/sub\ dir /home/user/ifs/0\ img.jpg /home/user/ifs/1\ img.jpg /home/user/ifs/2\ img.jpg
+ feh --title Slideshow -rFx -S name -D 1 /home/user/ifs/sub dir /home/user/ifs/0 img.jpg /home/user/ifs/1 img.jpg /home/user/ifs/2 img.jpg
text
#!/bin/sh
set -x
FILE_PATHS=`echo $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS | sed 's/ /\\\ /g' | sed 's/\\\ \// \//g'`
#FILE_PATHS=$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS
DELAY=1
logger -t slideshow  "feh --title Slideshow -rFx -S name -D $DELAY  $FILE_PATHS"
eval "feh --title \"Slideshow\" -rFx -S name -D $DELAY $FILE_PATHS"

+ sed s/\\ \// \//g
+ sed s/ /\\ /g
+ echo /home/user/ifs/sub dir /home/user/ifs/0 img.jpg /home/user/ifs/1 img.jpg /home/user/ifs/2 img.jpg
+ FILE_PATHS=/home/user/ifs/sub\ dir /home/user/ifs/0\ img.jpg /home/user/ifs/1\ img.jpg /home/user/ifs/2\ img.jpg
+ DELAY=1
+ logger -t slideshow feh --title Slideshow -rFx -S name -D 1 /home/user/ifs/sub\ dir /home/user/ifs/0\ img.jpg /home/user/ifs/1\ img.jpg /home/user/ifs/2\ img.jpg
+ eval feh --title "Slideshow" -rFx -S name -D 1 /home/user/ifs/sub\ dir /home/user/ifs/0\ img.jpg /home/user/ifs/1\ img.jpg /home/user/ifs/2\ img.jpg
+ feh --title Slideshow -rFx -S name -D 1 /home/user/ifs/sub dir /home/user/ifs/0 img.jpg /home/user/ifs/1 img.jpg /home/user/ifs/2 img.jpg
text
#!/bin/sh
set -x
FILE_PATHS=`echo $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS | sed 's/ /\\\ /g' | sed 's/\\\ \// \//g'`
#FILE_PATHS=$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS
DELAY=1
logger -t slideshow  "feh --title Slideshow -rFx -S name -D $DELAY  $FILE_PATHS"
feh --title "Slideshow" -rFx -S name -D $DELAY "$@"

+ sed s/\\ \// \//g
+ sed s/ /\\ /g
+ echo /home/user/ifs/sub dir /home/user/ifs/0 img.jpg /home/user/ifs/1 img.jpg /home/user/ifs/2 img.jpg
+ FILE_PATHS=/home/user/ifs/sub\ dir /home/user/ifs/0\ img.jpg /home/user/ifs/1\ img.jpg /home/user/ifs/2\ img.jpg
+ DELAY=1
+ logger -t slideshow feh --title Slideshow -rFx -S name -D 1 /home/user/ifs/sub\ dir /home/user/ifs/0\ img.jpg /home/user/ifs/1\ img.jpg /home/user/ifs/2\ img.jpg
+ feh --title Slideshow -rFx -S name -D 1 sub dir 0 img.jpg 1 img.jpg 2 img.jpg

тот же скрипт, с выделением одного файла

+ sed s/\\ \// \//g
+ sed s/ /\\ /g
+ echo /home/user/ifs/0 img.jpg
+ FILE_PATHS=/home/user/ifs/0\ img.jpg
+ DELAY=1
+ logger -t slideshow feh --title Slideshow -rFx -S name -D 1 /home/user/ifs/0\ img.jpg
+ feh --title Slideshow -rFx -S name -D 1 0 img.jpg

Благодарю за внимание!
Удачи!

#316221
bormant написал:

1) Для начала почитайте man bash на предмет IFS. Это набор символов, которые являются разделителями, по умолчанию это пробел, табуляция, конец строки. Именно по ним bash разделяет параметры командной строки, чтобы затем передать их запускаемому процессу (да-да, тот самый второй параметр char **argv в функции main()).

Это очевидно и об этом я даже никогда и не задумывался. Точнее - всегда принимал как данность и из этого исходил

bormant написал:

2) Затем смотрим документацию по Nautilus-у в части $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS, увидим там упоминание того, что файлы в списке разделены символом LF (Line Feed -- перевод строки, \n).

А вот здесь - или врут, или я чего-то не понимаю. Выгоняю содержимое $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS эхом в файл и вижу, что там между параметрами, между файлами никакого LF нет и в помине. Голимый пробел (0х20) и никакого намёка на 0x0A. Или echo заменяет LF на пробел? Поэтому я и кувыркался вокруг пробелов внутри имен и между ними. То есть пытался заэкранировать пробел в имени файла (чтобы имя было единой тушкой для аргумента командной строки) и оставить эти пробелы между именами, чтобы они были различными параметрами.

bormant написал:

3) Складываем (1) и (2), устанавливаем в качестве разделителя только перевод строки:
IFS="
"
после этого bash при запуске при формировании параметров поделит их по разрыву строки -- именно то, что и было нужно.

Значит не врали. Но догадаться об этом (что там не пробел) наблюдая за стандартным выводом $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS нереально.

#316231
27874 написал:

Ваш оригинальный скрипт, из первого сообщения.
...
Убираю лишний backquote

Моя вина. Этот backquote - досадная опечатка, возникшая при переносе текста скрипта в конфу. Невнимательно при этом соединил два sed'а в конвейер (в оригинале перед переносом sed'ы были в разных строках, после переноса так же объеденил их в один конвейер). Приношу за это свои извинения.
С eval'ом тоже всё работает. Из обсуждения вынес много пользы. Из чтения того же мана по eval'у совершенно неочевидно, что его следует применять, если надо исполнить какую либо программу, которая должна получить аргументы с заэкранированными пробелами. Тем более, что если пробелов нет, то и без eval'а нормально работает. Это есть тонкость, которую только от знатоков и можно почерпнуть.

#316236

В принципе можно и без sed обойтись средствами bash
как то так:

text
#!/bin/sh
FILE_PATHS=${NAUTILUS_SCRIPT_SELECTED_FILE_PATHS// /\ }

DELAY=1 echo "$FILE_PATHS" >temp.txt logger -t slideshow "feh --title Slideshow -rFx -S name -D $DELAY $FILE_PATHS" feh --title "Slideshow" -rFx -S name -D $DELAY "$@"

Войдите или зарегистрируйтесь, чтобы ответить.