Применить расширение скобок в «обратном порядке»


21

Например {a..c}{1..3}расширяется до a1 a2 a3 b1 b2 b3 c1 c2 c3.

Если бы я хотел напечатать a1 b1 c1 a2 b2 c2 a3 b3 c3, есть ли аналогичный способ сделать это? Какой самый простой способ?

Ответы:


30

Вы могли бы сделать:

$ eval echo '{a..c}'{1..3}
a1 b1 c1 a2 b2 c2 a3 b3 c3

Который затем говорит оболочке оценить:

echo {a..c}1 {a..c}2 {a..c}3

10

В этом конкретном случае я думаю, что вариант, предложенный Стефаном Шазеласом, является лучшим.

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

$ printf '%s\0' {a..c}{1..3} | sort -zk 1.2,1.2 | tr '\0' ' '

который возвращает:

a1 b1 c1 a2 b2 c2 a3 b3 c3

Кажется немного грязным, но теперь у меня есть огромный контроль в порядке, просто меняя два символа в команде выше; например:

$ echo {a..b}{1..2}{a..b}{1..2}

это расширится до:

a1a1 a1a2 a1b1 a1b2 a2a1 a2a2 a2b1 a2b2 b1a1 b1a2 b1b1 b1b2 b2a1 b2a2 b2b1 b2b2

Предположим, я хочу все 1во втором расширении, а затем 2:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.2,1.2 | tr '\0' ' '
a1a1 a1a2 a1b1 a1b2 b1a1 b1a2 b1b1 b1b2 a2a1 a2a2 a2b1 a2b2 b2a1 b2a2 b2b1 b2b2

Предположим, я хочу все aв третьем расширении, а затем b:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.3,1.3 | tr '\0' ' '
a1a1 a1a2 a2a1 a2a2 b1a1 b1a2 b2a1 b2a2 a1b1 a1b2 a2b1 a2b2 b1b1 b1b2 b2b1 b2b2

Предположим, я хочу все 1в четвертом расширении, а затем 2:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.4,1.4 | tr '\0' ' '
a1a1 a1b1 a2a1 a2b1 b1a1 b1b1 b2a1 b2b1 a1a2 a1b2 a2a2 a2b2 b1a2 b1b2 b2a2 b2b2

Предположим, я хочу, чтобы все было 1aпосередине 1b, затем 2a, затем 2b:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.2,1.3 | tr '\0' ' '
a1a1 a1a2 b1a1 b1a2 a1b1 a1b2 b1b1 b1b2 a2a1 a2a2 b2a1 b2a2 a2b1 a2b2 b2b1 b2b2

Вы также можете легко отменить любой порядок в приведенных выше расширениях, просто добавив rк предыдущей команде; например, последний:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -rzk 1.2,1.3 | tr '\0' ' '
b2b2 b2b1 a2b2 a2b1 b2a2 b2a1 a2a2 a2a1 b1b2 b1b1 a1b2 a1b1 b1a2 b1a1 a1a2 a1a1

Примечание_1 : обычно, если это последнее расширение будет использоваться в качестве списка аргументов, конечный пробел не является проблемой; но если вы хотите избавиться от него, вы можете добавить, например, любую из приведенных выше команд| sed 's/ $//' ; или даже| sed 's/ $/\n/', чтобы изменить это место дляnewline

Примечание_2 : В приведенных выше примерах я использовал подмножества из двух элементов (то есть: {a, b} и {1,2} ) просто для простоты в доказательстве концепции: вы можете использовать подмножества любой конечной длины, а соответствующая команда, будет сопоставимой.


5

баш, кш, зш

Один вкладыш, который работает в (bash, ksh, zsh) (не все оболочки могут выполнять «расширение скобок» в обратном порядке):

$ echo {3..1}{c..a} | rev
a1 b1 c1 a2 b2 c2 a3 b3 c3

Альтернатива, которая использует eval(которая все еще для bash, ksh, zsh и может быть более загадочной):

$ eval echo '{a..c}'{1..3}
a1 b1 c1 a2 b2 c2 a3 b3 c3

Чтобы понять, что происходит, замените evalна echo:

$ echo echo '{a..c}'{1..3}
echo {a..c}1 {a..c}2 {a..c}3

Выполненная команда (после раскрытия eval) на самом деле echo {a..c}1 {a..c}2 {a..c}3. Который расширяется, как вы хотите / нужно.

все снаряды

Существует несколько оболочек без «расширений скобок», поэтому невозможно использовать их для «всех оболочек». Нам нужен цикл (с завершающим пробелом):

$ for i in 1 2 3; do for j in a b c; do printf "%s%s " "$j" "$i"; done; done; echo
a1 b1 c1 a2 b2 c2 a3 b3 c3 

Если вам не нужно добавлять завершающий пробел:

s=""
for i in 1 2 3; do
    for j in a b c; do
        printf "%s%s%s" "$s" "$j" "$i"
        s=" "
    done
done
echo

Печать

a1 b1 c1 a2 b2 c2 a3 b3 c3

Если вам нужно сделать это для многих значений, нам нужно использовать нечто подобное расширению фигурных скобок, чтобы сгенерировать список чисел $(seq 10). И, поскольку seq не может генерировать список букв, нам нужно преобразовать в ascii сгенерированные числа:

s=""
for i in $(seq 4); do
    for j in $(seq 5); do
        printf "%s\\$(printf %03o $((96+j)))%s" "$s" "$i"
        s=" "
    done
done
echo

печатает:

a1 b1 c1 d1 e1 a2 b2 c2 d2 e2 a3 b3 c3 d3 e3 a4 b4 c4 d4 e4

Вы также можете добавить yash -o braceexpandв список.
Стефан Шазелас

@ StéphaneChazelas Я не уверен, что должен. Команда yash -o braceexpand -c 'echo {3..1}{c..a}'печатает 3{c..a} 2{c..a} 1{c..a}в Linux. Не полное «расширение скобки».
Исаак

3
{a..c}1 {a..c}2 {a..c}3

Расширения скобок в {a..c}{1..3}раскрываются слева направо, поэтому сначала вы получаете, a{1..3} b{1..3} c{1..3}а затем буквы объединяются с числами вa1 a2 a3 b1 b2 b3 c1 c2 c3 . Чтобы получить заказ, который вы хотите, вам придется использовать немного более длинное выражение выше.


Если вы хотите сделать это для большого диапазона «чисел», это не будет практичным.
RUBEN GONÇALO MOROUÇO

3
@ RUBENGONÇALOMOROUÇO Нет, не будет, и если вы делаете это для большого диапазона чисел, я бы предложил использовать альтернативный подход, например, двойной цикл. Это сработало бы для многих тысяч комбинаций, тогда как в некоторых контекстах расширение скобки вызывает мой триггер «список аргументов слишком длинный».
Кусалананда

2

Используя цикл:

for n in {1..3}; do printf '%s\n' {a..c}"$n"; done

Это зациклит ваше первое расширение, а затем расширит каждого персонажа вторым.

Если вам нужен вывод всего в одну строку, вы можете удалить \n:

for n in {1..3}; do printf '%s ' {a..c}"$n"; done

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


1
Почему так много голосов "за"?
Исаак

Должно быть, я неправильно прочитал вопрос. Обновлено
Jesse_b

2

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

Измените порядок расширений скобок, затем поменяйте местами символы:

echo {1..3}{a..c} | sed -E 's/(.)(.)( ?)/\2\1\3/g'

@ Муру: Ой. Исправлена. Спасибо.
Приостановлено до дальнейшего уведомления.


0

Одним из простых методов будет использование сортировки (1.2,1.2 означает, что вы берете один символ на второй позиции и заканчиваете в том же месте).

$ for i in {a..c}{1..3}; do echo $i; done|sort -n -k1.2,1.2
a1
b1
c1
a2
b2
c2
a3
b3
c3

Если вы хотите, чтобы они были в одной строке, вы можете использовать tr следующим образом:

$ for i in {a..c}{1..3}; do echo $i; done|sort -n -k1.2,1.2|tr '\n' ' '
a1 b1 c1 a2 b2 c2 a3 b3 c3

-2

Сделано по методу ниже

for i in {1..10}; do for j in {a..c}; do echo $j$i; done; done| perl -pne "s/\n/ /g"

выход

a1 b1 c1 a2 b2 c2 a3 b3 c3 a4 b4 c4 a5 b5 c5 a6 b6 c6 a7 b7 c7 a8 b8 c8 a9 b9 c9 a10 b10 c10

рассмотреть такжеfor i in {1..10}; do for j in {a..c}; do printf '%s ' "$j$i"; done; done;echo
Джефф Шаллер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.