LINUX.ORG.RU

bash, пройтись по строчкам с пробелами и выполнить произвольную комманду

 


1

1

Пример задачи - оставить только уникальные записи в хистори.

Ищется простой, лаконичный способ на каждый день, для разнообразных однострочников.

Пока, самый короткий вариант таков:

while read -r; do printf '%s\n' "$REPLY"; done < <(history | sed "s/^[ ]*[0-9]*  //g" | sort | uniq)

★★★★★

Последнее исправление: pon4ik (всего исправлений: 7)

cat history | python3 -c 'import sys; t=sys.stdin.readlines(); s=set(); print("\n".join((i[:-1],s.add(i))[0] for i in t if i not in s))'
Красота же.

evilface ★★
()
Ответ на: комментарий от dada

А у меня в redhat - почему-то нет :)

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

А то, что первый столбец в хистори это уникальное чиселко?

Сортируй/фильтруй по не-первому столбцу.
//KO

Kroz ★★★★★
()
Последнее исправление: Kroz (всего исправлений: 1)
Ответ на: комментарий от anonymous

Ну, это решает целевую задачу, но, хочется уже общего решения для проблемы пробелов :)

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

Какая ещё проблема с пробелами? Так что ли:

while read l; do echo "$l"; done <<< "$(history | sed "s/^[ ]*[0-9]*  //g" | sort | uniq)"
P.S. В выводе history ещё может быть timestamp.

xaizek ★★★★★
()
Ответ на: комментарий от pon4ik

Достойнее предыдущего тем, что считывает построчно, а не загружает весь вывод в память. А ещё тем, что не теряет множественные пробелы и пробелы в начале и конце.

doushiyou
()
Ответ на: комментарий от pon4ik
Here Strings

A variant of here documents, the format is:

       <<<word

The word undergoes brace expansion, tilde expansion, parameter and variable expansion, command substitution, arithmetic expansion, and quote removal.  Pathname expansion and word splitting are not  performed.
The result is supplied as a single string to the command on its standard input.

Но вариант с named-pipe на самом деле лучше скорее всего, мне просто <<< как-то лучше запомнилось, а про то забываю.

xaizek ★★★★★
()

Если я правильно понял формат файла у вас такой:

12 text1
11 text1
87 text2

и нужно получить

text1
text2
Тогда поможет вот такой скрипт
cat history | sed "s%^[0-9]\{1,\} \{1,\}%%g" | sort | uniq

r0ck3r ★★★★★
()
Последнее исправление: r0ck3r (всего исправлений: 2)
Ответ на: комментарий от r0ck3r

Нет не такой :)

скорее такой:

1 cat history | sed "s%^[0-9]\{1,\} \{1,\}%%g" | sort | uniq
2 cat history | sed "s%^[0-9]\{1,\} \{1,\}%%g" | sort | uniq

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от doushiyou

Вот я и спрашиваю — зачем? Они же там сугубо эстетические, и их именно что надо срезать, не?

Но если так хочется их сохранить, то, может быть, просто *не включать* разбивку на слова при чтении:

while read -r; do
    printf '%s\n' "$REPLY";
done < \
     <(history)
Zmicier ★★★★★
()

bash, пройтись по строчкам с пробелами и выполнить произвольную комманду
Пример задачи - оставить только уникальные записи в хистори.
Пока, самый короткий вариант таков:
while IFS= read -r line; do echo "$line"; done < <(history | sed "s/^[ ]*[0-9]* //g" | sort | uniq)

Пример совершенно непонятен. Нахрена здесь вообще этот цикл?

Zmicier ★★★★★
()
Ответ на: комментарий от Zmicier

Не знал, да. Но когда захочешь заменить REPLY на что-нибудь более осмысленное (такой же вложенный цикл сделаешь, например), уже и забудешь про IFS. Или нет. Сколько же всяких неочевидностей в этих башах, а.

doushiyou
()
Ответ на: комментарий от Zmicier

Цикл, для того что бы иметь возможность заменить echo на произвольную команду. А echo - чисто для демонстрации.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от doushiyou

Сколько же всяких неочевидностей

Тот же принцип «implicit is better than explicit», который унаследовал и широко развил Перл.

Читать поток построчно, и при этом не разбирать его почти никогда не нужно.

Zmicier ★★★★★
()

Кстати, все, что мы тут понаписали — неверно, ибо основано на ложной посылке, что каждая запись в истории занимает ровно одну строку.

Zmicier ★★★★★
()
Последнее исправление: Zmicier (всего исправлений: 1)
Ответ на: комментарий от pon4ik

в какой точке включается разбивка?

Указанием переменной, в какую писать первое и оставшиеся *слова*. В инструкции это действительно как-то хитро́ описано:

`read'
          read [-ers] [-a ANAME] [-d DELIM] [-i TEXT] [-n NCHARS]
              [-N NCHARS] [-p PROMPT] [-t TIMEOUT] [-u FD] [NAME ...]

     One line is read from the standard input, or from the file
     descriptor FD supplied as an argument to the `-u' option, and the
     first word is assigned to the first NAME, the second word to the
     second NAME, and so on, with leftover words and their intervening
     separators assigned to the last NAME.  If there are fewer words
     read from the input stream than names, the remaining names are
     assigned empty values.  The characters in the value of the `IFS'
     variable are used to split the line into words using the same
     rules the shell uses for expansion (described above in *note Word
     Splitting::).  The backslash character `\' may be used to remove
     any special meaning for the next character read and for line
     continuation.  If no names are supplied, the line read is assigned
     to the variable `REPLY'.  The return code is zero, unless
     end-of-file is encountered, `read' times out (in which case the
     return code is greater than 128), a variable assignment error
     (such as assigning to a readonly variable) occurs, or an invalid
     file descriptor is supplied as the argument to `-u'.

(info "(bash) Bash Builtins")

Zmicier ★★★★★
()
Ответ на: комментарий от Zmicier

неверно, ибо основано на ложной посылке, что каждая запись в истории занимает ровно одну строку.

Разве? Ну допустим, многострочниками я готов пожертвовать (задача всё таки не про хистори) :) Но насколько я заметил, то что попадает туда конкатенированное \ - превращается в одну строку, а то что идёт через ; в принципе не важно в каком порядке будет в истории.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

что попадает туда конкатенированное \ - превращается в одну строку

А это и есть одна строка. \ — оператор не конкатенации, но экранирования, в частности перевода строки.

А вот если его не экранировать, то ничего не превращается:

$ history -c
$ while read -r; do
>     printf '%s\n' "$REPLY";
> done
foo
foo
bar
bar
$ history
   70  while read -r; do
    printf '%s\n' "$REPLY";
done
   71  history
Zmicier ★★★★★
()
Ответ на: комментарий от Zmicier

Ну парсить видимо на «сообщения».

никак

Жаль, думал уже тайное знание по простым конечным автоматам на баш расскажешь мне ты :)

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от Zmicier

Тот же принцип «implicit is better than explicit», который унаследовал и широко развил Перл.

фиговый принцип, лучше наоборот

anonymous
()
Ответ на: комментарий от pon4ik

вроде работает:

[r0ck3r@desktop temp]$ cat history | sed "s%^[0-9]\{1,\} \{1,\}%%g" | sort | uniq
cat history | sed "s%^[0-9]\{1,\} \{1,\}%%g" | sort | uniq
[r0ck3r@desktop temp]$ 

Или должно быть что-то другое?

r0ck3r ★★★★★
()
Последнее исправление: r0ck3r (всего исправлений: 1)
Ответ на: комментарий от r0ck3r

А теперь, выполни echo для каждой строчки.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

Жаль, думал уже тайное знание по простым конечным автоматам

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

$ a=(
>   81  foo
>   82  bar
>   83  baz
>)
$ history
   80  a=(
   81  foo
   82  bar
   83  baz
)
   81  history

Zmicier ★★★★★
()
Последнее исправление: Zmicier (всего исправлений: 1)
Ответ на: комментарий от pon4ik

А есть идеи/знание как такое парсить в шелл?

В zsh массив $history есть для этого

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.