LINUX.ORG.RU

Форматирование вывода bash.

 ,


1

3

Есть скрипт, который на выходе выводит строки из двух или трех полей.
Пример неформатированного вывода может быть такой:

1 Name http://example.com/
21 Name 2 http://example.com/
337 Name (AB) AB http://example.com/
300016 Name5 http://example.com/
Name (name) http://example.com/

Т.е. первое поле содержит (или не содержит) номер, второе имя, третье адрес в интернете. Задача - сформировать три ровных столбца. Как не мучаюсь, полностью ровно сделать не выходит. Получается так:

1       Name            http://example.com/
21      Name 2          http://example.com/
337     Name (AB) AB            http://example.com/
300016  Name5           http://example.com/
        Name (name)             http://example.com/

Пример скрипта, который полностью эмулирует ситуацию:

#!/bin/bash
cat << EOF > columns932
1 Name http://example.com/
21 Name 2 http://example.com/
337 Name (AB) AB http://example.com/
300016 Name5 http://example.com/
Name (name) http://example.com/
EOF

cat columns932 | while read i; do
        NUM=`echo $i | sed -n 's/\(^[0-9]*\) .*/\1/p'`
        NAME=`echo $i | sed -n 's/^[0-9]* *\(.*\) http.*/\1/p'`
        HTTP=`echo $i | sed -n 's/.* \(.*$\)/\1/p'`
        echo -n-e "$NUM\t$NAME\t\t$HTTP \n"
done
Вопрос, какими командами оформить вывод трех переменных $NUM, $NAME и $HTTP, чтоб столбцы были ровными?



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

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

column -t в этом случае дает:

1  Name  http://example.com/
21  Name  2  http://example.com/
337  Name  (AB)  AB  http://example.com/
300016  Name5  http://example.com/
Name  (name)  http://example.com/

А надо получить так:

1       Name             http://example.com/
21      Name  2          http://example.com/
337     Name  (AB)  AB   http://example.com/
300016  Name5            http://example.com/
        Name  (name)     http://example.com/

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

Я бы тогда так сделал:
Башем расставил границы:

% cat testT 
1 | Name |  http://example.com/
21 | Name  2 |  http://example.com/
337 | Name  (AB)  AB |  http://example.com/
300016 | Name5 |  http://example.com/
 | Name (name) |  http://example.com/
и
% cat testT 
1 | Name |  http://example.com/
21 | Name  2 |  http://example.com/
337 | Name  (AB)  AB |  http://example.com/
300016 | Name5 |  http://example.com/
 | Name (name) |  http://example.com/

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

б

 % cat testT | column -t  -s '|'    
1         Name               http://example.com/
21        Name  2            http://example.com/
337       Name  (AB)  AB     http://example.com/
300016    Name5              http://example.com/
          Name (name)        http://example.com/

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

Меняю предпоследнюю строку на

echo "$NUM|$NAME|$HTTP"
На выходе получаю
1|Name|http://example.com/
21|Name 2|http://example.com/
337|Name (AB) AB|http://example.com/
300016|Name5|http://example.com/
|Name (name)|http://example.com/

Добавляю column

echo "$NUM|$NAME|$HTTP" | column -t -s '|'

На выходе получаю

1  Name  http://example.com/
21  Name 2  http://example.com/
337  Name (AB) AB  http://example.com/
300016  Name5  http://example.com/
Name (name)  http://example.com/

mech
() автор топика

А лучше не натягивать сову на глобус, а взять нормальный скиптовый ЯП.

$ cat 1.rb 
rows = STDIN.read.lines.map {|line| line.match(/^([0-9]*)\s*(.+)\s*(http:\/\/.*)/)[1..3]}
col_sizes = rows.transpose.map {|col| col.reduce(0){|memo, val| [memo, val.size].max} }

puts rows.map {|row|
	row.each_with_index.map {|val, i| val + ' ' * (col_sizes[i] - val.size)}.join(" ")
}.join("\n")
$ ruby 1.rb < columns932 
1      Name          http://example.com/
21     Name 2        http://example.com/
337    Name (AB) AB  http://example.com/
300016 Name5         http://example.com/
       Name (name)   http://example.com/

На bash + sed эквивалентный алгоритм накостылять тоже можно, но зачем?

Deleted
()
Ответ на: комментарий от mech
-echo "$NUM|$NAME|$HTTP" | column -t -s '|'
+echo "$NUM|$NAME|$HTTP" | column -t -s '|' -n
anonymous
()
Ответ на: комментарий от mech

Проблема в том, что column работает с файлом целиком. Если ему подсовывать отдельные строки, он будет выводить отдельные столбцы для каждой строки, ничего не помня о предыдущих.
Как один из костылей можно изменить конец скрипта так:

        echo "$NUM | $NAME | $HTTP" >> tmp234
done
cat tmp234 | column -t -s '|'
Тогда работает, но тем не менее это костыль.

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

Пробел перед стенкой и |, те.:

% diff testT testT2 
5c5
<  |Name (name)|http://example.com/
---
> |Name (name)|http://example.com/
cat testT | column -t -s '|'                                   
1       Name          http://example.com/
21      Name 2        http://example.com/
337     Name (AB) AB  http://example.com/
300016  Name5         http://example.com/
        Name (name)   http://example.com/
% cat testT2 | column -t -s '|'
1            Name                 http://example.com/
21           Name 2               http://example.com/
337          Name (AB) AB         http://example.com/
300016       Name5                http://example.com/
Name (name)  http://example.com/

anonymous
()
Ответ на: комментарий от mech
#!/bin/bash

echo '
1 Name http://example.com/
21 Name 2 http://example.com/
337 Name (AB) AB http://example.com/
300016 Name5 http://example.com/
Name (name) http://example.com/
' |
sed 's~\([0-9]*\)\s*\(.*\)\s*\(http://.*\)~\1|\2|\3~' |
column -t -s '|' -n
anonymous
()
Ответ на: комментарий от mech
oto@oto:(~) cat list.txt
1 # Name # # http://example.com/
21 # Name # 2 # http://example.com/
337 # Name # (AB)  AB # http://example.com/
300016 # Name5 # # http://example.com/
# # Name # (name)  # http://example.com/
oto@oto:(~) cat list.txt | column -t -s'#'
1         Name                 http://example.com/
21        Name     2           http://example.com/
337       Name     (AB)  AB    http://example.com/
300016    Name5                http://example.com/
          Name     (name)      http://example.com/

щитай столбики, программа только их и отображает, она не телепат чтоб все твои хотелки удовлетворять

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

А ok - не сразу понял в чём дело. Ну, хз. Мне кажется, вполне укладывается [данные -> сериализация (по сути csv c del '|') -> view], ну или вот rb скриптец.

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

Я же не зря в самом начале привел исходник, эмулирующий ситуацию. Попробуй, и твой (и всех остальных здесь отписавшихся, кроме скрипта .rb) пример там не сработает. Причину, почему не сработает, я написал выше.
Пытался сформировать вывод с помощью prinf на подобие printf «%6s %-35s%-60s\n» - тоже неверный вариант.

mech
() автор топика
1        | Name            | http://example.com/
21       | Name 2          | http://example.com/
337      | Name (AB) AB    | http://example.com/
300016   | Name5           | http://example.com/
         | Name (name)     | http://example.com/
printf "%-8s | %-15s | %s\n" "$NUM" "$NAME" "$HTTP"
Deleted
()

наверное ты хотел этого

$ echo "1 Name http://example.com/
21 Name 2 http://example.com/
337 Name (AB) AB http://example.com/
300016 Name5 http://example.com/
Name (name) http://example.com/" |\
sed -r 's/^[0-9]+\s+/&☣/;tl;s/.*/☣&/;:l;/^[^☣]{10}/bm;s/☣/ &/;bl;:m;s/☣//;s/.* /&☣/;:n;/^[^☣]{40}/{s/☣//;b};s/☣/ &/;bn'
1         Name                          http://example.com/
21        Name 2                        http://example.com/
337       Name (AB) AB                  http://example.com/
300016    Name5                         http://example.com/
          Name (name)                   http://example.com/

PS: люблю перед сном написать что-нить упоротое…

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

Стоит там появиться чем-то вроде (с русскими символами) «300097 Тесттесттесттес AB http://example.com", как этот столбец сбивается.
printf »%-8s | %-22s | %s\n" «$NUM» «$NAME» «$HTTP»

337      | Name (AB) AB           | http://example.com/
300016   | Name5                  | http://example.com/
300097   | Тесттесттесттес AB | http://example.com
         | Name (name)            | http://example.com/

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

поменяй местами HTTP и NAME. В HTTP кириллица ведь не планируется?))

Deleted
()
Ответ на: комментарий от emulek

Изначально парсится огромный .xml, из которого выбираются нужные строки, ну а дальше выдираются три параметра: номер, имя и адрес. Поэтому вывод формируется построчно.
Тот пример, что выше на sed тоже не будет работать, потому что это будет работать только тогда, когда все строки уже находятся в одном файле. Если они выводятся по одной, и после каждой строки стоит column -t -s '|' -n , то общая картина будет беспорядочная. В общем самое простое решение - использование промежуточного файла и полные его вывод в самом конце, типа такого:


#!/bin/bash
cat columns | while read i; do
        ....
        echo "$NUM|$NAME|$HTTP" >> tmp234 
done
cat tmp234 | column -t -s '|'

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

Тот пример, что выше на sed тоже не будет работать

мой пример работает тупо построчно.

1. формируется первая колонка в 10 символов в начале строки, пустая, либо с числом.

2. формируется вторая колонка шириной 40 символов в начале строки. Однако эта колонка кончается первым непробельным символом, после которого нет пробелов.

Как недостаток, замечу, что пробелы добавляются по одному в цикле. Если строк 100500, то работать будет медленно. Можно исправить, но и так упорото.

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

Твой работает, я и не спорил )) А строк в исходном файле может быть больше, чем 100500 раз в пять, серьезно ))

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

Тот пример, что выше на sed тоже не будет работать, потому что это будет работать только тогда, когда все строки уже находятся в одном файле. Если они выводятся по одной, и после каждой строки стоит column -t -s '|' -n , то общая картина будет беспорядочная.

Самый простой способ — научиться пользоваться перенаправлениями:

( анализируем в круглых скобочках сколько угодно входных файлов) | наш-скрипт-форматирования 

Скрипту на вход поступит один непрерывный пайп. Без всяких временных файлов.

Deleted
()
Ответ на: комментарий от emulek

О, чёткий скрипт, респект. :D Тоже хотел написать циклом на sed, но поздно вечером не осилил вспомнить, как это делается, и ман читать лень было.

Deleted
()
Ответ на: комментарий от mech

можно не по 1 пробелу добавлять, а сразу добавить 10 пробелов, а потом выдернуть 10 первых символов. Это будет быстрее, но лень жеж.

emulek
()
Ответ на: комментарий от mech

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

Ну почему не взять python??

alozovskoy ★★★★★
()
sed -r 's/^([0-9]+\s)?(.+)(\shttp)/\1\t\2\t\3/' file
TGZ ★★★★
()
Ответ на: комментарий от mech

Пытался сформировать вывод с помощью prinf на подобие printf «%6s %-35s%-60s\n» - тоже неверный вариант.

Почему? Тут же фиксированные позиции. Всё работает

#!/bin/bash

cat << EOF > columns932
1 Name http://example.com/
21 Name 2 http://example.com/
337 Name (AB) AB http://example.com/
300016 Name5 http://example.com/
Name (name) http://example.com/
EOF

cat columns932 | while read i; do
        NUM=`echo $i | sed -n 's/\(^[0-9]*\) .*/\1/p'`
        NAME=`echo $i | sed -n 's/^[0-9]* *\(.*\) http.*/\1/p'`
        HTTP=`echo $i | sed -n 's/.* \(.*$\)/\1/p'`
#        echo -n -e "$NUM\t$NAME\t\t$HTTP \n"
	printf "%6s %-35s%-60s\n" "$NUM" "$NAME" "$HTTP"
done
sdio ★★★★★
()
Последнее исправление: sdio (всего исправлений: 2)
Ответ на: комментарий от Deleted

Более простое форматирование вывода плюс вангую жуткие костыли для парсинга xml в скрипте ТСа (по крайней мере я не нашел легкого варианта парсеров этого формата для bash).

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

Ниче страшного. Можно чтоб и не сьезжала. Тут уж какой вариант выберет.

Питон из за регекспов в конечном счете?

Deleted
()
Ответ на: комментарий от alozovskoy

А то что кириллица едет это ничего страшного?

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

Да и регекспами парсить тоже не очень хорошо на мой взгляд.

Не хорошо, но это оффтопик здесь.

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

Внутри либы тоже регэкспы. Почему б сразу не выбрать нужные данные, вместо ненужных процедур. Что здесь решается питоном, почему лучше?

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

Ну мне проще написать «покажи мне все значения <name>, <num> и <http> из всех <somethingtag>», чем городить регекспы. Плюс на выходе получаем более-менее стандартное решение для разных «вариантов» xml, которое, к тому же, можно не только написать, но и через какое-то время прочитать. Для большей убедительности предлагаю ТСу показать свой скрипт, а я напишу то же самое на python, посмотрим где проще\нагляднее.

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

Для большей убедительности предлагаю ТСу показать свой скрипт, а я напишу то же самое на python, посмотрим где проще\нагляднее.

А если из баша вызвать xsltproc, то ты проиграешь

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