Індексуйте рядок у bash


15

Як я можу посилатись на рядок за індексом у sh / bash? Тобто, в основному, розбиваючи його.

Я намагаюся позбавити 5 символів імені файлу. Усі імена мають структуру: name_nr_code. Я намагаюся видалити 5 буквено-цифрових кодів. name_nr_завжди 10 символів.

Чи є така річ, як;

for i in * ; do mv "$i" "$i"[:10] ; done


5
Чому bashтег, якщо ви просите shрішення?
Стефан Шазелас

Відповіді:


16

Просто, як це.

(удар)

for i in * ; do mv -- "$i" "${i:0:5}" ; done

Вуаля.

І пояснення з Посібника з розширеного сценарію Bash ( Розділ 10. Маніпуляція змінними ) (з додатковим NOTEвкладеним рядком, щоб виділити помилки в цьому посібнику):

Вилучення підрядків

${string:position}

Витягує підрядку з $stringat $position.

Якщо $stringпараметр дорівнює "*" або "@", це витягує позиційні параметри, починаючи з $position.

${string:position:length}

Витягує $lengthсимволи підрядки з $stringо $position.

NOTEвідсутні лапки навколо розширень параметрів! echoне слід використовувати для довільних даних.

stringZ=abcABC123ABCabc
#       0123456789.....
#       0-based indexing.

echo ${stringZ:0}                       # abcABC123ABCabc
echo ${stringZ:1}                       # bcABC123ABCabc
echo ${stringZ:7}                       # 23ABCabc 

echo ${stringZ:7:3}                     # 23A
                                        # Three characters of substring.


# Is it possible to index from the right end of the string?

echo ${stringZ:-4}                      # abcABC123ABCabc
# Defaults to full string, as in ${parameter:-default}.
# However . . . 

echo ${stringZ:(-4)}                    # Cabc
echo ${stringZ: -4}                     # Cabc
# Now, it works.
# Parentheses or added space "escape" the position parameter.

Аргументи позиції та довжини можуть бути "параметризовані", тобто представлені як змінна, а не як числова константа.


Якщо $stringпараметр дорівнює "*" або "@", це витягує максимум $lengthпозиційних параметрів, починаючи з $position.

echo ${*:2}          # Echoes second and following positional parameters.
echo ${@:2}          # Same as above.

echo ${*:2:3}        # Echoes three positional parameters, starting at second.

NOTE: expr substr- розширення GNU.

expr substr $string $position $length

Витягує $lengthсимволи $stringпочинаючи з $position.

stringZ=abcABC123ABCabc
#       123456789......
#       1-based indexing.

echo `expr substr $stringZ 1 2`           # ab
echo `expr substr $stringZ 4 3`           # ABC

NOTE: Це echoзайве і робить його ще менш надійним. Використовуйте expr substr + "$string1" 1 2.

NOTE: exprповернеться з ненульовим статусом виходу, якщо вихід 0 (або -0, 00 ...).


До речі. Книга присутня в офіційному сховищі Ubuntu як abs-guide.


Сказати "позиція" трохи вводить в оману, оскільки це насправді зміщення, що означає, що ${var:1}не повертає значення varз "1-ї позиції", а насправді з 2-ї.
Kusalananda

Це правда, але поки ви не згодні, там може бути нульова позиція. Що зі мною добре.

9

У POSIX sh,

  • "${var%?????}"буде $varпозбавлений останніх 5 символів хвостових (або , $varякщо $varмістить менше 5 символів)

  • "${var%"${var#??????????}"}"є першими 10 символами $var.

  • "${var%_*}"буде $varпозбавлений найкоротшою рядки , яка відповідає _*в кінці $var( foo_bar_baz-> foo_bar).
  • "${var%%_*}": однаковий, але найдовший матч замість найкоротшого ( foo_bar_baz-> foo).
  • якщо ви хотіли отримати foo_bar_: "${var%"${var##*_}"}"( ${var##pattern}це те саме, що ${var%%pattern}шукати візерунок на початку $varзамість кінця).

З zsh:

  • $var[1,-6] для першого символу до 6-го від кінця (тому всі, крім останніх 5).
  • $var[1,10] для перших 10 символів.

З ksh, bashабо zsh:

  • "${var:0:10}": перші 10 символів $var

З bashабо zsh:

  • "${var:0:-5}": всі, крім останніх 5 символів (видає помилку і виходить із сценарію, якщо $varвстановлено, але містить менше 5 символів, також коли $varце не встановлено з zsh).

Якщо вам потрібна shсумісність Борна , це зробити дуже важко надійно. Якщо ви можете гарантувати, що результат не закінчиться символами нового рядка, ви можете:

first_10=`expr " $var" : ' \(.{1,10\}\)'` # beware the exit status
                                          # may be non-zero if the
                                          # result is 0 or 0000000000

all_but_last_5=`expr " $var" : ' \(.*\).\{5\}'`

Також у вас буде обмеження довжини $var(в залежності від систем).

У всіх цих рішеннях, якщо вони $varмістять байти, які не можуть скласти частину дійсних символів, YMMV.


мій, вони дійсно придумали якийсь потворний синтаксис для цих брекетів.
кіт

2

shне дає вбудованого способу отримання підрядки з рядка (наскільки я бачу), але з bashвами можливо

${i:0:10}

Це дасть вам перші десять символів значення змінної i.

Загальний формат є ${variable:offset:length}.


2

Більшість оболонок підтримують якесь розширення параметрів, яке може вам допомогти. В bash, ви можете використовувати

substr=${string:4:5} # start at position 4, length 5.

В dash, компенсації не підтримуються, але ви можете використовувати провідні та кінцеві шаблони:

remove_first3=${string#???}
remove_last2=${string%??}

0

По-перше, не використовуйте forцикл для імен файлів.

Тоді щось подібне повинно допомогти.

find ./ -type f | while read filename ;do
  newfilename=$(echo ${filename}|cut -c 1-10)
  mv ${filename} ${newfilename}
done

3
Чому погано користуватися forіменами файлів?
choroba

Процитуйте свої змінні та використовуйте printfдля безпечності. ... і read -r.
Kusalananda

3
forПетля ОП була чудовою, за винятком, можливо, зниклого --. Я можу побачити принаймні 10 помилок у ваших 4 рядках коду! багато з яких добре відома погана практика, як припущення, що назви файлів є однорядковими, використовують ехо, відсутні лапки
Stéphane Chazelas
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.