Это очень короткая тестовая строка. Попробуйте что-то большее, чем размер буфера, используемый либо netcat
, либо socat
, и отправьте эту строку несколько раз из нескольких тестовых экземпляров; вот программа отправителя
, которая делает это:
#!/usr/bin/env expect
package require Tcl 8.5
set socket [lindex $argv 0]
set character [string index [lindex $argv 1] 0]
set length [lindex $argv 2]
set repeat [lindex $argv 3]
set fh [open "| socat - UNIX-CONNECT:$socket" w]
# avoid TCL buffering screwing with our results
chan configure $fh -buffering none
set teststr [string repeat $character $length]
while {$repeat > 0} {
puts -nonewline $fh $teststr
incr repeat -1
}
А затем пусковая установка
вызывает это кучу раз (25), используя разные тестовые символы большой длины (9999) кучу раз (100), чтобы, надеюсь, пройти через любую границу буфера:
#!/bin/sh
# NOTE this is a very bad idea on a shared system
SOCKET=/tmp/blabla
for char in a b c d e f g h i j k l m n o p q r s t u v w x y; do
./sender -- "$SOCKET" "$char" 9999 100 &
done
wait
Хм, у меня нет netcat
надеюсь nc
на Centos 7 будет достаточно:
$ nc -klU /tmp/blabla > /tmp/out
И тогда в другом месте мы передаем данные этому
$ ./launcher
Теперь наш /tmp/out
будет неудобным, так как нет новых строк (некоторые вещи буферизуются на основе новой строки, поэтому новые строки могут влиять на результаты теста, если это так, см. setbuf(3)
для возможности буферизации на основе строк), поэтому нам нужен код, который ищет изменение символа и подсчитывает длину предыдущей последовательности идентичных символов.
#include <stdio.h>
int main(int argc, char *argv[])
{
int current, previous;
unsigned long count = 1;
previous = getchar();
if (previous == EOF) return 1;
while ((current = getchar()) != EOF) {
if (current != previous) {
printf("%lu %c\n", count, previous);
count = 0;
previous = current;
}
count++;
}
printf("%lu %c\n", count, previous);
return 0;
}
О, мальчик С! Давайте скомпилируем и проанализируем наш вывод...
$ make parse
cc parse.c -o parse
$ ./parse < /tmp/out | head
49152 b
475136 a
57344 b
106496 a
49152 b
49152 a
38189 r
57344 b
57344 a
49152 b
$
Угу. Это выглядит неправильно. 9999*100
должно быть 999900 одной буквы подряд, а вместо этого мы получили... не то. a
и b
приступили к работе раньше, но похоже, что r
каким-то образом успели сделать первые шаги. Это планирование работы для вас. Другими словами, вывод поврежден. Как насчет конца файла?
$ ./parse < /tmp/out | tail
8192 l
8192 v
476 d
476 g
8192 l
8192 v
8192 l
8192 v
476 l
16860 v
$ echo $((9999 * 100 / 8192))
122
$ echo $((9999 * 100 - 8192 * 122))
476
$
Похоже, 8192 — это размер буфера в этой системе.В любом случае! Ваш тестовый ввод был слишком коротким, чтобы пройти за пределы длины буфера, и создает ложное впечатление, что многократная запись клиента в порядке. Увеличьте объем данных от клиентов, и вы увидите смешанный и, следовательно, поврежденный вывод.
Попробуйте следующее:
позволяет предположить, что содержимое хранится в файле "file"
cat file | sed -E 's/ / x/'
даст
CHrIS john xherzog 10001 Marketing
tim x johnson 10002 IT
ruth xbertha Hendric 10003 HR
christ jason hellan 10004 Marketing
Относительно того, зачем писать sed
вышеупомянутым способом, обратитесь к this
Предположим, что это {tab}
символов в интервале ...
Вы никогда не увидите табуляцию в ваших awk
или tr
, потому что он уже использовался в качестве символа разделителя полей cut
.
Похоже, вы пытаетесь заменить пустое поле на x
. В этом случае вы можете использовать такие конструкции:
#!/bin/bash
#
while IFS= read -r line
do
first=$(echo "$line" | awk -F$'\t' '{print $1}')
middle=$(echo "$line" | awk -F$'\t' '{print $2}')
last=$(echo "$line" | awk -F$'\t' '{print $3}')
id=$(echo "$line" | awk -F$'\t' '{print $4}')
dept=$(echo "$line" | awk -F$'\t' '{print $5}')
echo "First is ${first:-x}"
echo "Middle is ${middle:-x}"
echo "Last is ${last:-x}"
echo "Id is ${id:-x}"
echo "Dept is ${dept:-x}"
echo
done
Мы не можем разделить с помощью IFS = $ '\ t' read -r first middle last ...
потому что читает
] разбивается на пробелы (пробел, табуляция, новая строка), а не на отдельные экземпляры. (На самом деле это намного сложнее; полные сведения можно найти в разделе «Разделение слов» на странице руководства.)
Я не стал использовать echo "$ line" | cut -f1
и т. д., потому что, если cut
исчерпывает поля, он повторно использует последнее найденное.
В качестве альтернативы "$ {middle: -x}" вы можете присвоить переменной x
, если она не установлена конструкцией $ {middle: = x}
. Префикс с помощью команды no-op :
, если вы хотите, чтобы назначение происходило само по себе (а не как побочный эффект какой-либо другой команды):
: ${middle:=x}
echo "The middle is $middle" # Will be 'x' if it was unset
Предполагая, что файл изначально разделен таблицами:
$ cat -t file
CHrIS^Ijohn^Iherzog^I10001^IMarketing
tim^I^Ijohnson^I10002^IIT
ruth^Ibertha^IHendric^I10003^IHR
christ^Ijason^Ihellan^I10004^IMarketing
И предполагая, что задача состоит в том, чтобы вставить x
в любое пустое поле в колонке 2.
$ awk -F'\t' 'BEGIN { OFS = FS } $2 == "" { $2 = "x" } { print }' file
CHrIS john herzog 10001 Marketing
tim x johnson 10002 IT
ruth bertha Hendric 10003 HR
christ jason hellan 10004 Marketing
Сценарий awk
будет использовать табуляцию в качестве разделителей ввода и вывода, обнаружит любые пустые поля в столбце 2 и изменит их на x
.
Предполагается, что ваш "sed" понимает escape-последовательности "\ t" "\ n". На случай, если его нет, есть WA. Но это нарушит логику кода.
sed -e '
s/\t/\n/; # 1st field sep => \n , a char sure to not be in PS by definition
s/\n\t/\tx\t/; # incase 2nd field empty then we will see the \n\t else not
s/\n/\t/; # just incase 2nd fields was nonempty, preceding would fail so deal here
' yourfile
perl -F"\t" -pale '$F[1] || s/^\S+\t(?=\t)/$&x/' yourfile