Shell tips: различия между версиями
Sirmax (обсуждение | вклад) (Новая: Категория:Linux Категория:Shell Категория:Bash =Короткие заметки по shell-программированию=) |
Sirmax (обсуждение | вклад) |
||
(не показана 21 промежуточная версия этого же участника) | |||
Строка 3: | Строка 3: | ||
[[Категория:Bash]] |
[[Категория:Bash]] |
||
=Короткие заметки по shell-программированию= |
=Короткие заметки по shell-программированию= |
||
+ | |||
+ | ==еще пара примеров sed c комментариями== |
||
+ | <PRE> |
||
+ | cat access_log_users | sed 's/^\[\(.*\):.*\].*JCORESESSIONID=aes\(.*\)==;.*POST.*/\1 \2/g' |
||
+ | </PRE> |
||
+ | Несмотря на то что мануалов полно, замечу |
||
+ | \(.*\) - это то что будет записано в переменную \1. Другими словами строка [<B>ЧТО ТО 1</B>:.*].*JCORESESSIONID=aes<B>ЧТО ТО 2</B>==;.*POST.*<BR> |
||
+ | Будет заменена на строку <B>ЧТО ТО 1 ЧТО ТО 2</B> |
||
+ | <BR> |
||
+ | Достаточно просто . |
||
+ | Для строк, не попавших под регулярку никаких действий не будет. |
||
+ | |||
+ | ==Среднее, максимальное и минимальное значение столбца== |
||
+ | В переменной $2 - номер столбца, обратить внимаени на "одинакрные в дыойных" кавычки - это нужно что бы передать параметры из шелла в awk |
||
+ | <PRE> |
||
+ | AVG=`cat $1 | awk ' { avg += $"'"$2"'" } END { avg=avg/NR; print avg } '` |
||
+ | echo AVG=$AVG |
||
+ | MIN=`cat $1 | awk 'BEGIN { min = 0} { if ( $"'"$2"'" < min ) min = $"'"$2"'" } END { print min } '` |
||
+ | echo MIN=$MIN |
||
+ | MAX=`cat $1 | awk 'BEGIN { max = 0} { if ( $"'"$2"'" > max ) max = $"'"$2"'" } END { print max } '` |
||
+ | echo MAX=$MAX |
||
+ | </PRE> |
||
+ | |||
+ | == Передать переменные в SED и AWK == |
||
+ | <PRE> |
||
+ | $ echo "unix scripting" |
||
+ | unix scripting |
||
+ | </PRE> |
||
+ | === In SED: === |
||
+ | |||
+ | This is a general substitution. I am trying to replace "unix" with "BASH", so "unix scripting" will become "BASH scripting" |
||
+ | <PRE> |
||
+ | $ echo "unix scripting" | sed 's/unix/BASH/' |
||
+ | BASH scripting |
||
+ | </PRE> |
||
+ | |||
+ | Suppose, the text "BASH" is assigned to a variable called "var", now if I try to replace "unix" with "$var" in sed single quote notation, its not going to work as SED can't expand external variable in single quotes. |
||
+ | <PRE> |
||
+ | $ var="BASH"; echo "unix scripting" | sed 's/unix/$var/' |
||
+ | $var scripting |
||
+ | </PRE> |
||
+ | |||
+ | Try the same above with double quotes, this will work. |
||
+ | <PRE> |
||
+ | $ var="BASH"; echo "unix scripting" | sed "s/unix/$var/" |
||
+ | BASH scripting |
||
+ | </PRE> |
||
+ | |||
+ | === In AWK === |
||
+ | |||
+ | General substitution of "unix" with "BASH", will work. "unix scripting" will become "BASH scripting" |
||
+ | <PRE> |
||
+ | $ echo "unix scripting" | awk '{gsub(/unix/,"BASH")}; 1' |
||
+ | BASH scripting |
||
+ | </PRE> |
||
+ | |||
+ | "BASH" is assigned in variable "var". So the following substitution is not going to work. |
||
+ | <PRE> |
||
+ | $ var="BASH"; echo "unix scripting" | awk '{gsub(/unix/,"$var")}; 1' |
||
+ | $var scripting |
||
+ | </PRE> |
||
+ | |||
+ | Method1: See the "'" (double quote-single quote-double quote) before and after the variable var. |
||
+ | <PRE> |
||
+ | $ var="BASH"; echo "unix scripting" | awk '{gsub(/unix/,"'"$var"'")}; 1' |
||
+ | BASH scripting |
||
+ | </PRE> |
||
+ | |||
+ | |||
+ | Method2: Use awk -v flag this way. На солярисе работает только этот способ, как и на HP-UX. Для соляриса надо приментья нормальный awk - /usr/xpg4/bin/awk |
||
+ | <PRE> |
||
+ | $ var="BASH"; echo "unix scripting" | awk -v v="$var" '{sub(/unix/,v)}1' |
||
+ | BASH scripting |
||
+ | </PRE> |
||
+ | |||
+ | ==Округление== |
||
+ | <PRE> |
||
+ | 04:45:14-root@wiki:~$ printf "%0.f\n" 4.51 |
||
+ | 5 |
||
+ | </PRE> |
||
+ | |||
+ | |||
+ | |||
+ | ==while БЕЗ subshell== |
||
+ | Способ Ворона |
||
+ | <PRE> |
||
+ | |||
+ | exec 3<$TMP_FILE1 |
||
+ | # 0 -stdin ... 2 stderr, 3 - first free descriptor |
||
+ | while read -u 3 NUMBER TYPE |
||
+ | do |
||
+ | echo NUMBER=$NUMBER TYPE=$TYPE |
||
+ | done |
||
+ | </PRE> |
||
+ | ==Считываени нескольких переменных== |
||
+ | Пример 1: |
||
+ | <PRE> |
||
+ | cat /tmp/service-perfdata | grep ttiweb | grep APACHE_HTTPS | grep -v "CRITICAL" | awk '{ print $2" " $16}' | tail -5000 | while read DATE TIMEOUT |
||
+ | do |
||
+ | DATE1=`date -d @$DATE` |
||
+ | TIMEOUT1=`echo $TIMEOUT |awk -F"[" '{ print $2 }'` |
||
+ | echo $DATE1"|"$TIMEOUT1 |
||
+ | done |
||
+ | </PRE> |
||
+ | |||
+ | Но для чтения инужно явно делать саб-шелл |
||
+ | Пример 2: |
||
+ | <PRE> |
||
+ | for FILE in `ls -1 *.log` |
||
+ | do |
||
+ | echo $FILE | awk -F"-" '{ print $1" "$2" "$3" "$5 }' | ( read YYYY MM DD TIME |
||
+ | # Subshell fore read! |
||
+ | # echo $YYYY $MM $DD |
||
+ | TIME=`echo $TIME | awk -F"__" '{print $1}'` |
||
+ | hh=`echo $TIME | cut -b 1-2` |
||
+ | mm=`echo $TIME | cut -b 3-4` |
||
+ | D=`date +%s --date="${YYYY}/$MM/$DD $hh:$mm"` |
||
+ | TMPFILE=`mktemp` |
||
+ | cat $FILE | grep -v "USER" | head -10 > $TMPFILE |
||
+ | ) |
||
+ | done |
||
+ | </PRE> |
||
+ | |||
+ | ==if-els== |
||
+ | коротко можно желать например, так |
||
+ | <PRE> |
||
+ | function export_data |
||
+ | { |
||
+ | ( |
||
+ | $JAVA_HOME/bin/java -Xbootclasspath/a:$CLASSPATH -jar $BIARENGINE $CONFIG_FILE && ( |
||
+ | if test -f $BOXI_EXPORT_FILE |
||
+ | then |
||
+ | echo "${DATE_FOR_LOG_FILES}:BOXI Export=SUCCEEDED" | tee -a $SYNC_STATUS_FILE |
||
+ | clear_tmp |
||
+ | exit 0 |
||
+ | else |
||
+ | echo "${DATE_FOR_LOG_FILES}:BOXI Export=FAILED" | tee -a $SYNC_STATUS_FILE |
||
+ | fi ) |
||
+ | ) || ( echo "${DATE_FOR_LOG_FILES}:BOXI Export=FAILED" | tee -a $SYNC_STATUS_FILE ) |
||
+ | |||
+ | } |
||
+ | </PRE> |
||
+ | |||
+ | |||
+ | ==find== |
||
+ | |||
+ | For example: |
||
+ | <PRE> |
||
+ | find . -mtime 0 # find files modified between now and 1 day ago |
||
+ | # (i.e., within the past 24 hours) |
||
+ | find . -mtime -1 # find files modified less than 1 day ago |
||
+ | # (i.e., within the past 24 hours, as before) |
||
+ | find . -mtime 1 # find files modified between 24 and 48 hours ago |
||
+ | find . -mtime +1 # find files modified more than 48 hours ago |
||
+ | |||
+ | find . -mmin +5 -mmin -10 # find files modified between |
||
+ | # 6 and 9 minutes ago |
||
+ | </PRE> |
||
+ | Using the "-printf" action instead of the default "-print" is useful to control the output format better than you can with ls or dir. You can use find with -printf to produce output that can easily be parsed by other utilities or imported into spreadsheets or databases. See the man page for the dozens of possibilities with the -printf action. (In fact find with -printf is more versatile than ls and is the preferred tool for forensic examiners even on Windows systems, to list file information.) For example the following displays non-hidden (no leading dot) files in the current directory only (no subdirectories), with an custom output format: |
||
+ | <PRE> |
||
+ | find . -maxdepth 1 -name '[!.]*' -printf 'Name: %16f Size: %6s\n' |
||
+ | </PRE> |
||
+ | "-maxdepth" is a Gnu extension. On a modern, POSIX version of find you could use this: |
||
+ | |||
+ | find . -path './*' -prune ... |
||
+ | |||
+ | On any version of find you can use this more complex (but portable) code: |
||
+ | <PRE> |
||
+ | find . ! -name . -prune ... |
||
+ | </PRE> |
||
+ | which says to "prune" (don't descend into) any directories except ".". |
||
+ | |||
+ | Note that "-maxdepth 1" will include "." unless you also specify "-mindepth 1". A portable way to include "." is: |
||
+ | <PRE> |
||
+ | find . \( -name . -o -prune \) ... |
||
+ | </PRE> |
||
+ | [This information posted by Stephane Chazelas, on 3/10/09 in newsgroup comp.unix.shell.] |
||
+ | |||
+ | As a system administrator you can use find to locate suspicious files (e.g., world writable files, files with no valid owner and/or group, SetUID files, files with unusual permissions, sizes, names, or dates). Here's a final more complex example (which I saved as a shell script): |
||
+ | <PRE> |
||
+ | find / -noleaf -wholename '/proc' -prune \ |
||
+ | -o -wholename '/sys' -prune \ |
||
+ | -o -wholename '/dev' -prune \ |
||
+ | -o -wholename '/windows-C-Drive' -prune \ |
||
+ | -o -perm -2 ! -type l ! -type s \ |
||
+ | ! \( -type d -perm -1000 \) -print |
||
+ | </PRE> |
||
+ | This says to seach the whole system, skipping the directories /proc, /sys, /dev, and /windows-C-Drive (presumably a Windows partition on a dual-booted computer). The Gnu -noleaf option tells find not to assume all remaining mounted filesystems are Unix file systems (you might have a mounted CD for instance). The "-o" is the Boolean OR operator, and "!" is the Boolean NOT operator (applies to the following criteria). |
||
+ | |||
+ | So these criteria say to locate files that are world writable ("-perm -2", same as "-o=w") and NOT symlinks ("! -type l") and NOT sockets ("! -type s") and NOT directories with the sticky (or text) bit set ("! \( -type d -perm -1000 \)"). (Symlinks, sockets and directories with the sticky bit set are often world-writable and generally not suspicious.) |
||
+ | |||
+ | A common request is a way to find all the hard links to some file. Using "ls -li file" will tell you how many hard links the file has, and the inode number. You can locate all pathnames to this file with: |
||
+ | <PRE> |
||
+ | find mount-point -xdev -inum inode-number |
||
+ | </PRE> |
||
+ | Since hard links are restricted to a single filesystem, you need to search that whole filesystem so you start the search at the filesystem's mount point. (This is likely to be either "/home" or "/" for files in your home directory.) The "-xdev" options tells find to not search any other filesystems. |
||
+ | |||
+ | (While most Unix and all Linux systems have a find command that supports the "-inum" criterion, this isn't POSIX standard. Older Unix systems provided the "ncheck" utility instead that could be used for this.) |
||
+ | |||
+ | |||
+ | ==Solaris-specific== |
||
+ | У find на солярисе нет ключа -mmin |
||
+ | иногда можно обойтись ключем |
||
+ | <PRE> |
||
+ | find $LOG_DIR/j2ee/FAM/ -newer /tmp/1/logs-fam-indusora-Fri-Aug-20.tar.bz2 |
||
+ | </PRE> |
||
+ | |||
+ | ==Копирование по сети== |
||
+ | Принимающяя: |
||
+ | <PRE> |
||
+ | nc -l 1234 | dd of=/tmp/image.img bs=4096 |
||
+ | </PRE> |
||
+ | Отдающая: |
||
+ | <PRE> |
||
+ | dd if=/dev/sda bs=4096 | nc 1.2.3.4 1234 |
||
+ | </PRE> |
||
+ | |||
+ | |||
+ | ==Примеры использования Awk== |
||
+ | с опеннета |
||
+ | |||
+ | Использование сокращений. |
||
+ | |||
+ | Конструкцию, используемую для вывода строк соответствующих заданной маске: |
||
+ | <PRE> |
||
+ | awk '{if ($0 ~ /pattern/) print $0}' |
||
+ | </PRE> |
||
+ | можно сократить до |
||
+ | <PRE> |
||
+ | awk '/pattern/' |
||
+ | </PRE> |
||
+ | Условие в awk может быть задано вне скобок, т.е. получаем: |
||
+ | <PRE> |
||
+ | awk '$0 ~ /pattern/ {print $0}' |
||
+ | </PRE> |
||
+ | По умолчанию, действия производятся со всей строкой, $0 можно не указывать: |
||
+ | <PRE> |
||
+ | awk '/pattern/ {print}' |
||
+ | </PRE> |
||
+ | |||
+ | print - является действием по умолчанию, его тоже можно не указывать. |
||
+ | <PRE> |
||
+ | awk '/pattern/' |
||
+ | </PRE> |
||
+ | Для вывода значения первого столбца строки, в которой присутствует маска LEGO: |
||
+ | <PRE> |
||
+ | awk '/LEGO/ {print $1}' |
||
+ | </PRE> |
||
+ | Для вывода значения первого столбца строки, во втором столбце которой присутствует маска LEGO: |
||
+ | <PRE> |
||
+ | awk '$2 ~ /LEGO/ {print $1}' |
||
+ | </PRE> |
||
+ | Для замены слова LIGO на LEGO и вывода только измененных строк можно использовать: |
||
+ | <PRE> |
||
+ | awk '{if(sub(/LIGO/,"LEGO")){print}}' |
||
+ | </PRE> |
||
+ | Но есть нужно выводить все строки (как sed 's/LIGO/LEGO/'), конструкцию можно упростить |
||
+ | <PRE> |
||
+ | (1 - true для всех строк): |
||
+ | awk '{sub(/LIGO/,"LEGO")}1' |
||
+ | </PRE> |
||
+ | Вывести все строки, за исключением каждой шестой: |
||
+ | <PRE> |
||
+ | awk 'NR % 6' |
||
+ | </PRE> |
||
+ | Вывести строки, начиная с 6 (как tail -n +6 или sed '1,5d'): |
||
+ | <PRE> |
||
+ | awk 'NR > 5' |
||
+ | </PRE> |
||
+ | Вывести строки, в которых значение второго столбца равно foo: |
||
+ | <PRE> |
||
+ | awk '$2 == "foo"' |
||
+ | </PRE> |
||
+ | Вывести строки, в которых 6 и более столбцов: |
||
+ | <PRE> |
||
+ | awk 'NF >= 6' |
||
+ | </PRE> |
||
+ | Вывести строки, в которых есть слова foo и bar: |
||
+ | <PRE> |
||
+ | awk '/foo/ && /bar/' |
||
+ | </PRE> |
||
+ | Вывести строки, в которых есть слово foo, но нет bar: |
||
+ | <PRE> |
||
+ | awk '/foo/ && !/bar/' |
||
+ | </PRE> |
||
+ | Вывести строки, в которых есть слова foo или bar (как grep -e 'foo' -e 'bar'): |
||
+ | <PRE> |
||
+ | awk '/foo/ || /bar/' |
||
+ | </PRE> |
||
+ | Вывести все непустые строки: |
||
+ | <PRE> |
||
+ | awk 'NF' |
||
+ | </PRE> |
||
+ | Вывести все строки, удалив содержимое последнего столбца: |
||
+ | <PRE> |
||
+ | awk 'NF--' |
||
+ | </PRE> |
||
+ | Вывести номера строк перед содержимым: |
||
+ | <PRE> |
||
+ | awk '$0 = NR" "$0' |
||
+ | </PRE> |
||
+ | Заменим команды (пропускаем 1 строку, фильтруем строки с foo и заменяем foo на bar, |
||
+ | затем переводим в верхний регистр и выводим значение второго столбца) |
||
+ | <PRE> |
||
+ | cat test.txt | head -n +1 | grep foo | sed 's/foo/bar/' | tr '[a-z]' '[A-Z]' | cut -d ' ' -f 2 |
||
+ | </PRE> |
||
+ | аналогичной конструкцией на awk: |
||
+ | <PRE> |
||
+ | cat test.txt | awk 'NR>1 && /foo/{sub(/foo/,"bar"); print toupper($2)}' |
||
+ | </PRE> |
||
+ | |||
+ | Использование диапазонов. |
||
+ | |||
+ | Вывести группу строк, начиная со строки, в которой есть foo, и заканчивая |
||
+ | строкой, в которой есть bar: |
||
+ | <PRE> |
||
+ | awk '/foo/,/bar/' |
||
+ | </PRE> |
||
+ | Исключив из вывода строки с вхождением заданных масок: |
||
+ | <PRE> |
||
+ | awk '/foo/,/bar/{if (!/foo/ && !/bar/)print}' |
||
+ | </PRE> |
||
+ | Более оптимальный вариант: |
||
+ | <PRE> |
||
+ | awk '/bar/{p=0};p;/foo/{p=1}' |
||
+ | </PRE> |
||
+ | Исключить только строку с завершающим вхождением (bar) |
||
+ | <PRE> |
||
+ | awk '/bar/{p=0} /foo/{p=1} p' |
||
+ | </PRE> |
||
+ | Исключить только строку с начальным вхождением (foo) |
||
+ | <PRE> |
||
+ | awk 'p; /bar/{p=0} /foo/{p=1}' |
||
+ | </PRE> |
||
+ | |||
+ | Разбиение файла по шаблонам. |
||
+ | |||
+ | Имеется файл (file), в котором группы строк разделены шаблонами FOO1,FOO2 и т.д. |
||
+ | Необходимо записать данные, находящиеся между метками FOO в разные файлы, |
||
+ | соответствующие указанным в FOO номерам. |
||
+ | <PRE> |
||
+ | awk -v n=1 '/^FOO[0-9]*/{close("out"n);n++;next} {print > "out"n}' file |
||
+ | </PRE> |
||
+ | В GNU Awk можно сделать так: |
||
+ | <PRE> |
||
+ | LC_ALL=C gawk -v RS='FOO[0-9]*\n' -v ORS= '{print > "out"NR}' file |
||
+ | </PRE> |
||
+ | |||
+ | Парсинг CSV. |
||
+ | |||
+ | По умолчанию в качестве разделителя используются пробел и табуляция. |
||
+ | Чтобы определить иной разделитель, например запятую, нужно использовать FS=',' или опцию "-F". |
||
+ | В качестве параметра может быть задано регулярное выражение, например, FS='^ *| *, *| *$' |
||
+ | Но для разбора CSV это не подойдет, так как пробелы могут присутствовать и внутри трок, |
||
+ | поэтому проще вырезать лидирующие пробелы перед и после запятой: |
||
+ | <PRE> |
||
+ | FS=',' |
||
+ | for(i=1;i<=NF;i++){ |
||
+ | gsub(/^ *| *$/,"",$i); |
||
+ | print "Field " i " is " $i; |
||
+ | } |
||
+ | </PRE> |
||
+ | Если в CSV данные помещены в кавычки, например "field1","field2", то подойдет такой скрипт: |
||
+ | <PRE> |
||
+ | FS=',' |
||
+ | for(i=1;i<=NF;i++){ |
||
+ | gsub(/^ *"|" *$/,"",$i); |
||
+ | print "Field " i " is " $i; |
||
+ | } |
||
+ | </PRE> |
||
+ | Но скрипт придется усовершенствовать для разбора полей вида: |
||
+ | <PRE> |
||
+ | field1, "field2,with,commas" , field3 , "field4,foo" |
||
+ | |||
+ | $0=$0","; |
||
+ | while($0) { |
||
+ | match($0,/[^,]*,| *"[^"]*" *,/); |
||
+ | sf=f=substr($0,RSTART,RLENGTH); |
||
+ | gsub(/^ *"?|"? *,$/,"",f); |
||
+ | print "Field " ++c " is " f; |
||
+ | sub(sf,""); |
||
+ | } |
||
+ | </PRE> |
||
+ | |||
+ | Проверка IPv4 адреса. |
||
+ | <PRE> |
||
+ | awk -F '[.]' 'function ok(n) { |
||
+ | return (n ~ /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$/) |
||
+ | } |
||
+ | {exit (ok($1) && ok($2) && ok($3) && ok($4))}' |
||
+ | |||
+ | </PRE> |
||
+ | Сравнение двух файлов. |
||
+ | |||
+ | Вывод всех дублирующихся строк из двух неотсортированных файлах file1 и file2: |
||
+ | <PRE> |
||
+ | awk '!($0 in a) {c++;a[$0]} END {exit(c==NR/2?0:1)}' file1 file2 |
||
+ | </PRE> |
||
+ | |||
+ | Вывод только выделенных блоков текста. |
||
+ | |||
+ | Например, чтобы показать из файла с текстом только текст, отмеченный как =текст= |
||
+ | можно использовать: |
||
+ | <PRE> |
||
+ | awk -v RS='=' '!(NR%2)' |
||
+ | </PRE> |
||
+ | с форматированием переносов строк: |
||
+ | <PRE> |
||
+ | awk -v RS='=' '!(NR%2){gsub(/\n/," ");print}' |
||
+ | </PRE> |
||
+ | ==Ссылки== |
||
+ | * http://www.f-notes.info/linux:sed |
||
+ | * http://archiv.kiev1.org/page-687.html |
Текущая версия на 21:30, 27 мая 2011
Короткие заметки по shell-программированию
еще пара примеров sed c комментариями
cat access_log_users | sed 's/^\[\(.*\):.*\].*JCORESESSIONID=aes\(.*\)==;.*POST.*/\1 \2/g'
Несмотря на то что мануалов полно, замечу
\(.*\) - это то что будет записано в переменную \1. Другими словами строка [ЧТО ТО 1:.*].*JCORESESSIONID=aesЧТО ТО 2==;.*POST.*
Будет заменена на строку ЧТО ТО 1 ЧТО ТО 2
Достаточно просто .
Для строк, не попавших под регулярку никаких действий не будет.
Среднее, максимальное и минимальное значение столбца
В переменной $2 - номер столбца, обратить внимаени на "одинакрные в дыойных" кавычки - это нужно что бы передать параметры из шелла в awk
AVG=`cat $1 | awk ' { avg += $"'"$2"'" } END { avg=avg/NR; print avg } '` echo AVG=$AVG MIN=`cat $1 | awk 'BEGIN { min = 0} { if ( $"'"$2"'" < min ) min = $"'"$2"'" } END { print min } '` echo MIN=$MIN MAX=`cat $1 | awk 'BEGIN { max = 0} { if ( $"'"$2"'" > max ) max = $"'"$2"'" } END { print max } '` echo MAX=$MAX
Передать переменные в SED и AWK
$ echo "unix scripting" unix scripting
In SED:
This is a general substitution. I am trying to replace "unix" with "BASH", so "unix scripting" will become "BASH scripting"
$ echo "unix scripting" | sed 's/unix/BASH/' BASH scripting
Suppose, the text "BASH" is assigned to a variable called "var", now if I try to replace "unix" with "$var" in sed single quote notation, its not going to work as SED can't expand external variable in single quotes.
$ var="BASH"; echo "unix scripting" | sed 's/unix/$var/' $var scripting
Try the same above with double quotes, this will work.
$ var="BASH"; echo "unix scripting" | sed "s/unix/$var/" BASH scripting
In AWK
General substitution of "unix" with "BASH", will work. "unix scripting" will become "BASH scripting"
$ echo "unix scripting" | awk '{gsub(/unix/,"BASH")}; 1' BASH scripting
"BASH" is assigned in variable "var". So the following substitution is not going to work.
$ var="BASH"; echo "unix scripting" | awk '{gsub(/unix/,"$var")}; 1' $var scripting
Method1: See the "'" (double quote-single quote-double quote) before and after the variable var.
$ var="BASH"; echo "unix scripting" | awk '{gsub(/unix/,"'"$var"'")}; 1' BASH scripting
Method2: Use awk -v flag this way. На солярисе работает только этот способ, как и на HP-UX. Для соляриса надо приментья нормальный awk - /usr/xpg4/bin/awk
$ var="BASH"; echo "unix scripting" | awk -v v="$var" '{sub(/unix/,v)}1' BASH scripting
Округление
04:45:14-root@wiki:~$ printf "%0.f\n" 4.51 5
while БЕЗ subshell
Способ Ворона
exec 3<$TMP_FILE1 # 0 -stdin ... 2 stderr, 3 - first free descriptor while read -u 3 NUMBER TYPE do echo NUMBER=$NUMBER TYPE=$TYPE done
Считываени нескольких переменных
Пример 1:
cat /tmp/service-perfdata | grep ttiweb | grep APACHE_HTTPS | grep -v "CRITICAL" | awk '{ print $2" " $16}' | tail -5000 | while read DATE TIMEOUT do DATE1=`date -d @$DATE` TIMEOUT1=`echo $TIMEOUT |awk -F"[" '{ print $2 }'` echo $DATE1"|"$TIMEOUT1 done
Но для чтения инужно явно делать саб-шелл Пример 2:
for FILE in `ls -1 *.log` do echo $FILE | awk -F"-" '{ print $1" "$2" "$3" "$5 }' | ( read YYYY MM DD TIME # Subshell fore read! # echo $YYYY $MM $DD TIME=`echo $TIME | awk -F"__" '{print $1}'` hh=`echo $TIME | cut -b 1-2` mm=`echo $TIME | cut -b 3-4` D=`date +%s --date="${YYYY}/$MM/$DD $hh:$mm"` TMPFILE=`mktemp` cat $FILE | grep -v "USER" | head -10 > $TMPFILE ) done
if-els
коротко можно желать например, так
function export_data { ( $JAVA_HOME/bin/java -Xbootclasspath/a:$CLASSPATH -jar $BIARENGINE $CONFIG_FILE && ( if test -f $BOXI_EXPORT_FILE then echo "${DATE_FOR_LOG_FILES}:BOXI Export=SUCCEEDED" | tee -a $SYNC_STATUS_FILE clear_tmp exit 0 else echo "${DATE_FOR_LOG_FILES}:BOXI Export=FAILED" | tee -a $SYNC_STATUS_FILE fi ) ) || ( echo "${DATE_FOR_LOG_FILES}:BOXI Export=FAILED" | tee -a $SYNC_STATUS_FILE ) }
find
For example:
find . -mtime 0 # find files modified between now and 1 day ago # (i.e., within the past 24 hours) find . -mtime -1 # find files modified less than 1 day ago # (i.e., within the past 24 hours, as before) find . -mtime 1 # find files modified between 24 and 48 hours ago find . -mtime +1 # find files modified more than 48 hours ago find . -mmin +5 -mmin -10 # find files modified between # 6 and 9 minutes ago
Using the "-printf" action instead of the default "-print" is useful to control the output format better than you can with ls or dir. You can use find with -printf to produce output that can easily be parsed by other utilities or imported into spreadsheets or databases. See the man page for the dozens of possibilities with the -printf action. (In fact find with -printf is more versatile than ls and is the preferred tool for forensic examiners even on Windows systems, to list file information.) For example the following displays non-hidden (no leading dot) files in the current directory only (no subdirectories), with an custom output format:
find . -maxdepth 1 -name '[!.]*' -printf 'Name: %16f Size: %6s\n'
"-maxdepth" is a Gnu extension. On a modern, POSIX version of find you could use this:
find . -path './*' -prune ...
On any version of find you can use this more complex (but portable) code:
find . ! -name . -prune ...
which says to "prune" (don't descend into) any directories except ".".
Note that "-maxdepth 1" will include "." unless you also specify "-mindepth 1". A portable way to include "." is:
find . \( -name . -o -prune \) ...
[This information posted by Stephane Chazelas, on 3/10/09 in newsgroup comp.unix.shell.]
As a system administrator you can use find to locate suspicious files (e.g., world writable files, files with no valid owner and/or group, SetUID files, files with unusual permissions, sizes, names, or dates). Here's a final more complex example (which I saved as a shell script):
find / -noleaf -wholename '/proc' -prune \ -o -wholename '/sys' -prune \ -o -wholename '/dev' -prune \ -o -wholename '/windows-C-Drive' -prune \ -o -perm -2 ! -type l ! -type s \ ! \( -type d -perm -1000 \) -print
This says to seach the whole system, skipping the directories /proc, /sys, /dev, and /windows-C-Drive (presumably a Windows partition on a dual-booted computer). The Gnu -noleaf option tells find not to assume all remaining mounted filesystems are Unix file systems (you might have a mounted CD for instance). The "-o" is the Boolean OR operator, and "!" is the Boolean NOT operator (applies to the following criteria).
So these criteria say to locate files that are world writable ("-perm -2", same as "-o=w") and NOT symlinks ("! -type l") and NOT sockets ("! -type s") and NOT directories with the sticky (or text) bit set ("! \( -type d -perm -1000 \)"). (Symlinks, sockets and directories with the sticky bit set are often world-writable and generally not suspicious.)
A common request is a way to find all the hard links to some file. Using "ls -li file" will tell you how many hard links the file has, and the inode number. You can locate all pathnames to this file with:
find mount-point -xdev -inum inode-number
Since hard links are restricted to a single filesystem, you need to search that whole filesystem so you start the search at the filesystem's mount point. (This is likely to be either "/home" or "/" for files in your home directory.) The "-xdev" options tells find to not search any other filesystems.
(While most Unix and all Linux systems have a find command that supports the "-inum" criterion, this isn't POSIX standard. Older Unix systems provided the "ncheck" utility instead that could be used for this.)
Solaris-specific
У find на солярисе нет ключа -mmin иногда можно обойтись ключем
find $LOG_DIR/j2ee/FAM/ -newer /tmp/1/logs-fam-indusora-Fri-Aug-20.tar.bz2
Копирование по сети
Принимающяя:
nc -l 1234 | dd of=/tmp/image.img bs=4096
Отдающая:
dd if=/dev/sda bs=4096 | nc 1.2.3.4 1234
Примеры использования Awk
с опеннета
Использование сокращений.
Конструкцию, используемую для вывода строк соответствующих заданной маске:
awk '{if ($0 ~ /pattern/) print $0}'
можно сократить до
awk '/pattern/'
Условие в awk может быть задано вне скобок, т.е. получаем:
awk '$0 ~ /pattern/ {print $0}'
По умолчанию, действия производятся со всей строкой, $0 можно не указывать:
awk '/pattern/ {print}'
print - является действием по умолчанию, его тоже можно не указывать.
awk '/pattern/'
Для вывода значения первого столбца строки, в которой присутствует маска LEGO:
awk '/LEGO/ {print $1}'
Для вывода значения первого столбца строки, во втором столбце которой присутствует маска LEGO:
awk '$2 ~ /LEGO/ {print $1}'
Для замены слова LIGO на LEGO и вывода только измененных строк можно использовать:
awk '{if(sub(/LIGO/,"LEGO")){print}}'
Но есть нужно выводить все строки (как sed 's/LIGO/LEGO/'), конструкцию можно упростить
(1 - true для всех строк): awk '{sub(/LIGO/,"LEGO")}1'
Вывести все строки, за исключением каждой шестой:
awk 'NR % 6'
Вывести строки, начиная с 6 (как tail -n +6 или sed '1,5d'):
awk 'NR > 5'
Вывести строки, в которых значение второго столбца равно foo:
awk '$2 == "foo"'
Вывести строки, в которых 6 и более столбцов:
awk 'NF >= 6'
Вывести строки, в которых есть слова foo и bar:
awk '/foo/ && /bar/'
Вывести строки, в которых есть слово foo, но нет bar:
awk '/foo/ && !/bar/'
Вывести строки, в которых есть слова foo или bar (как grep -e 'foo' -e 'bar'):
awk '/foo/ || /bar/'
Вывести все непустые строки:
awk 'NF'
Вывести все строки, удалив содержимое последнего столбца:
awk 'NF--'
Вывести номера строк перед содержимым:
awk '$0 = NR" "$0'
Заменим команды (пропускаем 1 строку, фильтруем строки с foo и заменяем foo на bar, затем переводим в верхний регистр и выводим значение второго столбца)
cat test.txt | head -n +1 | grep foo | sed 's/foo/bar/' | tr '[a-z]' '[A-Z]' | cut -d ' ' -f 2
аналогичной конструкцией на awk:
cat test.txt | awk 'NR>1 && /foo/{sub(/foo/,"bar"); print toupper($2)}'
Использование диапазонов.
Вывести группу строк, начиная со строки, в которой есть foo, и заканчивая строкой, в которой есть bar:
awk '/foo/,/bar/'
Исключив из вывода строки с вхождением заданных масок:
awk '/foo/,/bar/{if (!/foo/ && !/bar/)print}'
Более оптимальный вариант:
awk '/bar/{p=0};p;/foo/{p=1}'
Исключить только строку с завершающим вхождением (bar)
awk '/bar/{p=0} /foo/{p=1} p'
Исключить только строку с начальным вхождением (foo)
awk 'p; /bar/{p=0} /foo/{p=1}'
Разбиение файла по шаблонам.
Имеется файл (file), в котором группы строк разделены шаблонами FOO1,FOO2 и т.д. Необходимо записать данные, находящиеся между метками FOO в разные файлы, соответствующие указанным в FOO номерам.
awk -v n=1 '/^FOO[0-9]*/{close("out"n);n++;next} {print > "out"n}' file
В GNU Awk можно сделать так:
LC_ALL=C gawk -v RS='FOO[0-9]*\n' -v ORS= '{print > "out"NR}' file
Парсинг CSV.
По умолчанию в качестве разделителя используются пробел и табуляция. Чтобы определить иной разделитель, например запятую, нужно использовать FS=',' или опцию "-F". В качестве параметра может быть задано регулярное выражение, например, FS='^ *| *, *| *$' Но для разбора CSV это не подойдет, так как пробелы могут присутствовать и внутри трок, поэтому проще вырезать лидирующие пробелы перед и после запятой:
FS=',' for(i=1;i<=NF;i++){ gsub(/^ *| *$/,"",$i); print "Field " i " is " $i; }
Если в CSV данные помещены в кавычки, например "field1","field2", то подойдет такой скрипт:
FS=',' for(i=1;i<=NF;i++){ gsub(/^ *"|" *$/,"",$i); print "Field " i " is " $i; }
Но скрипт придется усовершенствовать для разбора полей вида:
field1, "field2,with,commas" , field3 , "field4,foo" $0=$0","; while($0) { match($0,/[^,]*,| *"[^"]*" *,/); sf=f=substr($0,RSTART,RLENGTH); gsub(/^ *"?|"? *,$/,"",f); print "Field " ++c " is " f; sub(sf,""); }
Проверка IPv4 адреса.
awk -F '[.]' 'function ok(n) { return (n ~ /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$/) } {exit (ok($1) && ok($2) && ok($3) && ok($4))}'
Сравнение двух файлов.
Вывод всех дублирующихся строк из двух неотсортированных файлах file1 и file2:
awk '!($0 in a) {c++;a[$0]} END {exit(c==NR/2?0:1)}' file1 file2
Вывод только выделенных блоков текста.
Например, чтобы показать из файла с текстом только текст, отмеченный как =текст= можно использовать:
awk -v RS='=' '!(NR%2)'
с форматированием переносов строк:
awk -v RS='=' '!(NR%2){gsub(/\n/," ");print}'