Заменить все вхождения символа, кроме первого [дубликат]

Это ошибка в syslog, но иллюстрирует общую проблему, когда человек удаляет файл, пока он открыт программой. Когда вы выполняете команду "rm ", вы удаляете запись каталога, но вы НЕ удаляете основной файл. Операционная система ведет подсчет ссылок на файл и не удаляет данные основного файла, пока счетчик ссылок не станет равным нулю. В случае среднего файла количество ссылок на неоткрытый файл равно единице (запись в каталоге). Когда файл открывается, счетчик увеличивается до двух. Если вторая программа откроет тот же файл, счетчик будет увеличен до трех. Если запись каталога удаляется, счетчик уменьшается до двух - это означает, что файл анонимичен (не имеет имени), но не будет удален, пока обе программы, открывшие его, не закроются - в этом случае ОС удалит основное дисковое хранилище, связанное с файлом.

Когда вы удаляете /var/log/mail, системный регистратор все еще имеет файл, открытый для записи. Если вы создадите новый /var/log/mail, он будет указывать на файл, отличный от того, который системный регистратор записывает в данный момент. Единственный способ привести все в соответствие - перезапустить системный регистратор. Когда первоначальный системный регистратор завершает работу, все связанные с ним файлы закрываются - включая журнал анонимной почты, запись в каталоге которого вы удалили. Когда вы перезапустите системный регистратор, он снова откроет /var/log/mail, когда ему понадобится записать сообщение журнала, и будет держать его открытым в дальнейшем.

Другой способ часто обнаруживается, когда запущенная программа заполняет весь диск данными файла; пользователь удаляет очень большой файл, но место на диске не освобождается, потому что файл все еще существует и занимает место на диске, но запись в каталоге была удалена. Когда программа завершится (либо потому, что пользователь завершил ее, либо она завершилась сама), дисковое пространство будет восстановлено, потому что количество ссылок на файл станет равным нулю.

Чтобы предотвратить это, регистратор может сначала записать сообщение журнала, проверить, существует ли запись в каталоге файла журнала, и если она не существует, закрыть исходный файл журнала, открыть новый, а затем переписать сообщение - чтобы оно не потерялось. Но для выполнения всего этого потребуется гораздо больше сложностей, чем нужно системному регистратору -- ведь каждое сообщение, которое он пишет, будет записываться гораздо дольше из-за дополнительной проверки каталога -- которая будет успешной каждый раз, когда файл НЕ был удален.

Для более ясного понимания всего вышесказанного, следующая команда является поучительной, поскольку она описывает системный вызов, который выполняет удаление записи каталога и декремент ссылки: "man 3 unlink"

9
07.09.2016, 09:16
5 ответов

Использование awk :

awk '{gsub(",", ";"); sub(";", ","); print}' file.txt
  • gsub (",", ";") заменяет все , на ;

  • sub ("; ",", ") заменяет первый ; на ,

Пример:

% cat file.txt
test1,test2,test3
test4,test5
test6,test7,test8,test9,test10
test11,test12,test13,test14

% awk '{gsub(",", ";"); sub(";", ","); print}' file.txt
test1,test2;test3
test4,test5
test6,test7;test8;test9;test10
test11,test12;test13;test14
2
27.01.2020, 20:04

Это можно сделать с помощью этого,

$ sed -e 's/,/;/g' -e 's/;/,/1' infile
test1,test2;test3
test4,test5
test6,test7;test8;test9;test10
test11,test12;test13;test14

Объяснение

  • s /, /; / g заменяет все вхождения , на ;

  • s /; /, / 1 заменяет первое вхождение ; на ,


Если у вас GNU sed , вы также можете попробовать это просто и удобно,

sed 's/,/;/2g' infile
14
27.01.2020, 20:04
$ cat ip.txt 
test1,test2,test3
test4,test5
test6,test7,test8,test9,test10
test11,test12,test13,test14
test15

$ perl -F, -ane 'print "$F[0]"; print ",".join(";",@F[1..$#F]) if($#F > 0)' ip.txt 
test1,test2;test3
test4,test5
test6,test7;test8;test9;test10
test11,test12;test13;test14
test15


Другой способ:

perl -F'/(,)/,$_,2' -ane '$F[2] =~ s/,/;/g; print @F'
  • /(,)/,$_,2 разделить $_ (строка ввода) на две части на основе , Поскольку используется (,), он захватывает и разделитель, в результате чего получается три элемента, как объяснено ниже
  • $F[0] получает первое поле, $F[1] получит ,, если присутствует
  • $F[2] получит оставшиеся поля, если присутствуют


Еще один способ, эмулирующий sed 's/,/; /2g'

perl -pe '$c=0; s/,/++$c<2 ? $& : ";"/ge' ip.txt
  • инициализируем счетчик для каждой строки
  • при замене проверяем значение счетчика по мере необходимости
  • модификатор e позволяет использовать Perl код в секции замены
2
27.01.2020, 20:04

Решение в TXR Lisp:

txr -e  '(each ((line (get-lines)))
           (set [line (rest (where (op eql #\,) line))]
                (repeat ";"))
           (put-line line))' < input

"Для каждой строки установите остальные места (т.е. все, кроме первого), где эта строка равна запятой, на точку с запятой. Выведите строку. "

Аннотированная расшифровка фоновой интерактивной сессии слушателя:

$ txr
This is the TXR Lisp interactive listener of TXR 148.
Use the :quit command or type Ctrl-D on empty line to exit.

Бесконечный ленивый список точек с запятой:

1> (take 3 (repeat ";"))
(#\; #\; #\;)

Связывание переменных:

2> (let ((a "a,b,c"))
     a)
"a,b,c"

Скобочная нотация извлекает индексы:

3> (let ((a "a,b,c"))
     [a '(1 3)])
",,"

функция where извлекает позиции, в которых предикат истинен:

4> (let ((a "a,b,c"))
     (where (op eql #\,) a))
(1 3)

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

5> (let ((a "a,b,c"))
     (set [a '(1 3)] (repeat ";"))
     a)
"a;b;c"

Динамически вычисляем индексы с помощью where:

6> (let ((a "a,b,c"))
     (set [a (where (op eql #\,) a)] (repeat ";"))
     a)
"a;b;c"

Вырезаем первый индекс с помощью rest, чтобы не сбивать первую запятую точкой с запятой:

7> (let ((a "a,b,c"))
     (set [a (rest (where (op eql #\,) a))] (repeat ";"))
     a)
"a,b;c"

Тестируем с другими строками. Упс, """ не модифицируется:

8> (let ((a ""))
     (set [a (rest (where (op eql #\,) a))] (repeat ";"))
     a)
** replace-str: "" of type lit is not a modifiable string
** during evaluation of form (sys:dwim-set a #:g0145 #:g0144)
** ... an expansion at expr-8:2 of (sys:dwim-set (#:g0146) #:g0145
                                                 #:g0144)
** which is located at expr-8:2

Попробуйте еще раз:

9> (let ((a (copy "")))
     (set [a (rest (where (op eql #\,) a))] (repeat ";"))
     a)
""

Строка без запятых:

10> (let ((a "a"))
     (set [a (rest (where (op eql #\,) a))] (repeat ";"))
     a)
"a"

Строка с одной запятой: замены не происходит.

11> (let ((a "a,b"))
     (set [a (rest (where (op eql #\,) a))] (repeat ";"))
     a)
"a,b"
1
27.01.2020, 20:04

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

$ sed 's/,/\n/g; s/\n/,/; s/\n/;/g' input
test1,test2;test3
test4,test5
test6,test7;test8;test9;test10
test11,test12;test13;test14

Поскольку sed читает входные данные построчно, в обычном вводе не будет символов новой строки. Поэтому мы можем заменить все запятые на новые строки и будем уверены, что путаницы не будет. Далее мы восстанавливаем первую новую строку обратно в запятую. И наконец, мы заменяем все оставшиеся новые строки точкой с запятой.

Более подробно:

  • s/,/\n/g заменяет все запятые на новые строки.

  • s/\n/,/ заменяет первую новую строку запятой.

  • s/\n/;/g заменяет все оставшиеся новые строки точками с запятой.

7
27.01.2020, 20:04

Теги

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