Замените пробелы в начале строки на «-»

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

echo *.jpg

, и он выдаст

*.jpg

. В частности, строка * .jpg остается неизменной. Однако если вы перейдете в каталог, содержащий файлы .jpg, например, предположим, что у нас есть два файла: image1.jpg и image2.jpg, тогда команда echo * .jpg не будет выводить

image1.jpg image2.jpg

, а * .jpg будет расширен .

Если вы наберете

find . -name *.jpg

и при этом в каталоге, в котором вы находитесь, нет файлов .jpg, тогда find получит аргументы «.», «-Name» и «* .jpg». Если, однако, вы наберете эту команду в каталоге, содержащем файлы .jpg, например image1.jpg и image2.jpg, тогда find получит аргументы «.», «-Name», «image1.jpg» и «image2.jpg». ", поэтому фактически запустит команду

find . -name image1.jpg image2.jpg

и find будет жаловаться. Что действительно может сбить с толку, если вы опустите кавычки, так это наличие одного файла .jpg (скажем, image1.jpg). Тогда расширение подстановочного знака приведет к

find . -name image1.jpg

, и команда find найдет все файлы с базовым именем image1.jpg.

В сторону: Это действительно приводит к полезной идиоме bash для проверки, соответствуют ли какие-либо файлы заданному шаблону:

if [ "$(echo *.jpg)" = "*.jpg" ]; then
    # *.jpg has no matches
else
    # *.jpg has matches
fi

хотя имейте в виду, что это не сработает, если в текущем каталоге есть файл с именем '* .jpg'. Для большей герметичности вы можете сделать

if [ "$(echo *.jpg)" = "*.jpg" ] && [ ! -e "*.jpg" ]; then
    # *.jpg has no matches
else
    # *.jpg has matches
fi

(Хотя это не имеет прямого отношения к вопросу, я добавил это, поскольку он иллюстрирует некоторые аспекты того, как работает расширение с подстановочными знаками.)

5
11.05.2017, 11:08
4 ответа

С sedвам понадобится цикл, например:

sed -e :1 -e 's/^\( *\) /\1-/; t1' < file

Или сделайте что-то вроде:

sed '
s/ */&\
/; # add a newline after the leading spaces
h; # save a copy on the hold space
y/ /-/; # replace *every* space with -
G; # append our saved copy
s/\n.*\n//; # remove the superflous part' < file

С помощью perlвы можете делать такие вещи, как:

perl -pe 's{^ *}{$& =~ y/ /-/r}e' < file

или

perl -pe 's/(^|\G) /-/g' < file

\Gв PCRE соответствует (с нулевой -шириной )в конце предыдущего совпадения (в контексте //g). Итак, здесь мы заменяем пробел, который следует либо за началом строки ^, либо за концом предыдущего совпадения (, то есть ранее замененным пробелом ).

(можно также работать с sedреализациями, которые поддерживают PCRE, напримерssed -R).

С помощью awkвы можете сделать что-то вроде:

awk '
  match($0, /^ +/) {
    space = substr($0, 1, RLENGTH)
    gsub(" ", "-", space)
    $0 = space substr($0, RLENGTH+1)
  }
  {print}' < file

Если вы хотите также преобразовать вкладки (, где, например, <space><tab>fooбудет преобразовано в --------foo), вы можете предварительно обработать ввод с помощью expand. С помощью GNU expandвы можете сделать это expand -i, чтобы преобразовывались только табуляции среди начальных пробелов в строке. Вы можете указать расстояние между позициями табуляции -(каждые 8 ​​столбцов по умолчанию )с помощью опции -t.

Обобщить это на все символы горизонтального интервала или, по крайней мере, на те, которые входят в категорию [:blank:]в вашей локали, становится сложнее.

Если бы не символ TAB, это был бы просто вопрос:

perl -Mopen=locale -MText::CharWidth=mbswidth -pe 's/^\h+/"-" x mbswidth($&)/e'

Но символ TAB, являющийся управляющим символом, имеет ширину -1с этим mbswidth(), в то время как на самом деле он имеет переменную ширину от 1 до 8 столбцов в зависимости от того, где он находится на линии.

Команда expandрасширяет его до правильного количества пробелов, но некоторые реализации, в том числе GNU expand, не понимают это правильно, когда есть многобайтовые символы -(как и все пустые символы, кроме табуляции, пробел в UTF -8 локалей ),и даже некоторые из тех, которые поддерживают многобайтовые символы -, могут быть обмануты символами нулевой -ширины или символами двойной ширины -(, такими как U+3000, который находится в классе [:blank:]в типичных локалях GNU, по крайней мере ). Таким образом, нужно было бы выполнить расширение TAB вручную, например:

perl -Mopen=locale -MText::CharWidth=mbswidth -pe 's{^\h+}{
  $s = $&;
  while ($s =~ /(.*?)\t(.*)/) {
    $s = $1. (" " x ((7-mbswidth($1)) % 8 + 1)). $2;
  }
  "-" x mbswidth($s)}e'
10
27.01.2020, 20:34

Работает

Мы устанавливаем цикл do-while и продолжаем преобразовывать последний пробел, смежный с первым не пробелом, пока строка все еще имеет начальный космос.

sed -e '
   :loop
      /^ /s/ \([^ ]\|$\)/-\1/
   tloop
' filename.ext


while IFS= read -r l; do
   read -r ll <<<"$(printf '%ss\n' "$l")"
   printf '%s%s\n' \
      "$(seq -s= 0 "$(expr "$l" : '[   ]*')" | tr = - | tr -cd -)" \
      "${ll%?}"
done < filename.ext

Результат

-wqdq
-wqdqgrhehr
-cnkzjncicoajc
-hello space
----oejwfoiwejfow
----wqodojw
----more spaces
----more
----
-
--

Работа

  • Настройте цикл while для построчного чтения файла с IFS, установленным в NULL. Это имеет целью сохранить все пробелы в строке.
  • Затем выполните фиктивное чтение той же строки со значением по умолчанию IFS. Это обрежет все начальные пробелы. Мы добавляем фиктивный символ, не являющийся новой строкой, в конце, чтобы предотвратить свертывание завершающих строк новой строки на этапе расширения команды. Мы снимаем его во время печати.
  • Целью команды expr является определение количества совпадений, в нашем случае пробелов в начале строки.
  • Используя это число, мы генерируем последовательность тире с помощью команд seq и tr с соответствующей настройкой.
  • Наконец, мы печатаем тире вместе с обрезанной строкой, т. е. строкой, считываемой через IFS по умолчанию.
0
27.01.2020, 20:34

Другой awkподход:

awk 'match($0, /^[[:space:]]+/){ p=""; l=RLENGTH; while(l--) p=p"-";
     sub(/^[[:space:]]+/,p); print}' yourfile

Выход:

-wqdq
-wqdqgrhehr
-cnkzjncicoajc
-hello space
----oejwfoiwejfow
----wqodojw
----more spaces
----more
----
-
--

match($0, /^[[:space:]]+/)-соответствует последовательности начальных пробелов

l=RLENGTH-размер совпадающей последовательности для каждой строки

while(l--) p=p"-"-построение замещающей подстроки



АльтернативныйPythonПодход 3.x:

пробел _- _hyphen.pyскрипт:

import sys, re
with open(sys.argv[1], 'r') as f:  # reading input file
    for l in f.read().splitlines():
        m = re.match(r'^ +', l)    # capture sequence of leading spaces 
        print(l if not m else l.replace(' ', '-', m.end()))

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

python3 space_to_hyphen.py yourfile
0
27.01.2020, 20:34

Стефан уже предоставил правильное sedрешение. Вот небольшая и немного более явная альтернатива Python 3:

#!/usr/bin/env python3
import sys

with open(sys.argv[1]) as f:
    for line in f:
        beginning = True
        for char in line:
            if beginning and char == " ":
                print("-",end="")
            else:
               beginning = False
               print(char,end="")

Пробный запуск:

# This is the input text
$ cat -A input.txt
 wqdq$
 wqdqgrhehr$
 cnkzjncicoajc$
 hello space$
    oejwfoiwejfow$
    wqodojw$
    more spaces$
    more$
    $
 $
  $

# And this is the output with the given python script
$./add_dashes.py./input.txt                                                                                            
-wqdq
-wqdqgrhehr
-cnkzjncicoajc
-hello space
----oejwfoiwejfow
----wqodojw
----more spaces
----more
----
-
--
0
27.01.2020, 20:34

Теги

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