Bash ls с двойной заменой

Стандартной командой для применения регулярных выражений к строковым аргументам является exprс оператором :. Он понимает стандартные основные регулярные выражения . Он выводит 1 или 0 в зависимости от того, соответствует ли регулярное выражение или нет, если регулярное выражение не имеет хотя бы одной группы захвата, и в этом случае он выводит то, что было сопоставлено первой группой захвата. Одной из особенностей exprв :является то, что регулярное выражение неявно привязывается к началу, как если бы оно начиналось с ^. Так:

text=1234
expr "x$text" : "x.\(.*\)"

Мы добавляем префиксx(к произвольному )как к тексту, так и к регулярному выражению, иначе команда завершится ошибкой, если содержимое $textокажется оператором expr.

Первый .соответствует первому символу $text, который не выводится, так как он не входит в группу захвата. Затем мы фиксируем остальные .*, состоящие из 0 или более символов, столько, сколько возможно для вывода.

Статус выхода будет не -нулевым, если регулярное выражение не соответствует($textпусто или начинается с чего-то, что не может быть интерпретировано как символ )или если вывод представляет собой число0(и в зависимости от реализации expr, некоторые из его различных вариантов написания, такие как 00, -0...)или пустая строка.

Но в любом случае вам не нужно запускать exprили использовать для этого регулярные выражения. Это могут сделать стандартные операторы раскрытия параметров оболочки :

.
text=1234
printf '%s\n' "${text#?}"

Где ${var#pattern}расширяется до содержимого $var, где начальная часть, соответствующая pattern, была удалена.

0
30.09.2020, 03:01
4 ответа

Это происходит потому, что он оценивается как несколько аргументов, а не только один. Попробуйте это:

ls "$@"

$@заменяет все параметры строками, разделенными пробелами.

-1
18.03.2021, 23:01

Например.sh:

#!/usr/bin/env bash
FILE_PATTERN="$1"
MATCHING_FILES=$(eval "echo $FILE_PATTERN")
echo "Files: $MATCHING_FILES"
ls -l $MATCHING_FILES

Это будет использовать первый аргумент для example.sh в качестве шаблона глобуса оболочки. Это, вероятно, сломается для имен файлов, содержащих пробелы или специальные символы. Как переменные должны заключаться в кавычки, чтобы избежать этого, но при этом применять шаблон, вам предстоит узнать.

Использование:

$./example.sh 'file-{x,y}.txt'
Files: file-x.txt file-y.txt
-rw-r--r--  1 user  group  0 30 Sep 16:47 file-x.txt
-rw-r--r--  1 user  group  0 30 Sep 16:47 file-y.txt

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

0
18.03.2021, 23:01

Я бы сделал:

#! /bin/bash -
sources=()
while getopts d:... o; do
  case $o in
    (d) sources+=("$OPTARG");;
    (...) ;;
  esac
done
shift "$((OPTIND - 1))"

ls -- "${sources[@]}"

То есть пусть ваш сценарий принимает список каталогов, где вызывающая сторона использует по одному -dдля каждого каталога.

Затем пользователь может:

my-script -d/home/{x,y}

, если их оболочка поддерживает раскрытие фигурных скобок, или что-то еще, что их оболочка предлагает для облегчения создания этого списка.

В rc/ fish/ zsh -o rcexpandparamэто может быть:

my-script -d$array

например (также -d$^arrayбез rcexpandparamвzsh)

Вzsh:

my-script /home/*(P[-d])

и т. д.

Чтобы ваш сценарий выполнял расширение скобок для аргумента, а не другие формы расширения, вы можете заключать в кавычки все последовательности символов, кроме {, },,.0-9, если хотите разрешить {1..10}стили )и прогоняем их через eval.

Было бы проще с zshили ksh93, так как bash${var//pattern/replacement}очень ограничен:

#! /bin/zsh -
source=${1?}

set -o extendedglob
eval "sources=( ${source//(#m)[^"{}".,0-9]##/'$MATCH'} )"

printf ' - "%s"\n' "$sources[@]"

Пример:

$ myscript 'x\{1..3}$$`{a b,c}'
 - "x\1$$`a b"
 - "x\1$$`c"
 - "x\2$$`a b"
 - "x\2$$`c"
 - "x\3$$`a b"
 - "x\3$$`c"

Но тогда пользователь больше не сможет передавать буквальный foo{a,b}аргумент.

0
18.03.2021, 23:01

Обычный способ сделать это — передать каталоги в качестве основных аргументов командной строки, не требуя флагов опций. т.е. пользователь запустит my-script -a -b foo /home/me/dir1 /some/other/path. После запуска getoptsдля анализа параметров у вас останутся каталоги в позиционных параметрах, и вы можете просто запустить ls "$@". Здесь пользователь может использовать любые расширения, которые позволяет его оболочка, для заполнения этих последних аргументов, будь то /some/path/{foo,bar}или /some/path/*.

В качестве альтернативы пользователь может передать несколько параметров -d, чтобы они выполняли my-script -a -b foo -d /home/me/dir1 -d /some/other/path. Здесь сложнее использовать расширения, так как им нужно будет добавлять -dперед каждым аргументом с тем, что позволяет их оболочка. Возможно, с чем-то вроде a=(/path/{this,that}); my-script -a -b foo "${a[@]/#/-d}".

В любом случае, копирование расширений оболочки в скрипте не является обычным делом и может застать ваших пользователей врасплох, поэтому я бы не советовал этого делать. Если вы сделаете это, вам также нужно будет решить , какие расширения поддерживать. Не все оболочки поддерживают все расширения, и не все пользователи знают обо всех расширениях, поддерживаемых каждой оболочкой.

1
18.03.2021, 23:01

Теги

Похожие вопросы