Как заменить вкладка с символом в файле

Это очень короткая тестовая строка. Попробуйте что-то большее, чем размер буфера, используемый либо 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 — это размер буфера в этой системе.В любом случае! Ваш тестовый ввод был слишком коротким, чтобы пройти за пределы длины буфера, и создает ложное впечатление, что многократная запись клиента в порядке. Увеличьте объем данных от клиентов, и вы увидите смешанный и, следовательно, поврежденный вывод.

0
23.02.2017, 18:58
4 ответа

Попробуйте следующее:

позволяет предположить, что содержимое хранится в файле "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

1
28.01.2020, 02:15

Предположим, что это {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
4
28.01.2020, 02:15

Предполагая, что файл изначально разделен таблицами:

$ 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.

1
28.01.2020, 02:15

Sed

Предполагается, что ваш "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

perl -F"\t" -pale '$F[1] || s/^\S+\t(?=\t)/$&x/' yourfile
0
28.01.2020, 02:15

Теги

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