Обрезать последние 3 символа строки БЕЗ использования sed или perl и т.д.

У меня есть оболочка script, выводящая данные следующим образом:

1234567890  *
1234567891  *

Мне нужно удалить JUST последние три символа "*". Я знаю, что могу сделать это через

(whatever) | sed 's/\(.*\).../\1/'

Но я НЕ хочу использовать sed для скорости. Он всегда будет одним и тем же последним 3 символами.

Любой быстрый способ очистки вывода?

Ответ 1

Предполагая, что все данные отформатированы как ваш пример, используйте 'cut', чтобы получить только первый столбец.

cat $file | cut -d ' ' -f 1  

или получить первые 10 символов.

cat $file | cut -c 1-10

Ответ 2

Вот старомодный трюк Unix для удаления последних трех символов из строки, которая не использует sed OR awk...

> echo 987654321 | rev | cut -c 4- | rev

987654

В отличие от предыдущего примера, использующего 'cut', это не требует знания длины строки.

Ответ 3

Я могу гарантировать, что bash не будет быстрее, чем sed для этой задачи. Запуск внешних процессов в bash - это, как правило, плохая идея, но только если вы делаете это много.

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

Однако вы можете обнаружить, что следующий sed будет немного быстрее, чем ваша версия:

(whatever) | sed 's/...$//'

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

Честно говоря, об единственном способе, которым я могу думать об этом, было бы быстрее - это создать собственную программу фильтрации на основе C. И единственная причина, которая может быть быстрее, чем sed, заключается в том, что вы можете воспользоваться дополнительными знаниями, которые у вас есть на ваших потребностях в обработке (sed должно допускать обобщенное шествие, поэтому из-за этого может быть медленнее).

Не забывайте оптимизационную мантру: "Измерьте, не угадайте!"


Если вы действительно хотите сделать это по одной строке за раз в bash (и я все еще утверждаю, что это плохая идея), вы можете использовать:

pax> line=123456789abc
pax> line2=${line%%???}
pax> echo ${line2}
123456789
pax> _

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

#!/usr/bin/bash

echo This is a pretty chunky line with three bad characters at the end.XXX >qq1
for i in 4 16 64 256 1024 4096 16384 65536 ; do
    cat qq1 qq1 >qq2
    cat qq2 qq2 >qq1
done

head -20000l qq1 >qq2
wc -l qq2

date
time sed 's/...$//' qq2 >qq1
date
head -3l qq1

и запустите его. Здесь вывод на мой (не очень быстрый) R40 ноутбук:

pax> ./chk.sh
20000 qq2
Sat Jul 24 13:09:15 WAST 2010

real    0m0.851s
user    0m0.781s
sys     0m0.050s
Sat Jul 24 13:09:16 WAST 2010
This is a pretty chunky line with three bad characters at the end.
This is a pretty chunky line with three bad characters at the end.
This is a pretty chunky line with three bad characters at the end.

Это 20 000 строк в секунду, довольно хорошо для чего-то, что делалось только каждый час.

Ответ 4

$ x="can_haz"
$ echo "${x%???}"
can_

Ответ 5

Оба awk и sed работают довольно быстро, но если вы считаете, что это важно, используйте одно из следующих действий:

Если символы, которые вы хотите удалить, всегда находятся в конце строки

echo '1234567890  *' | tr -d ' *'

Если они могут появляться в любом месте строки, и вы хотите удалить их в конце

echo '1234567890  *' | rev | cut -c 4- | rev

Манифестные страницы всех команд объяснят, что происходит.

Я думаю, что вы должны использовать sed.

Ответ 6

Примечание: Этот ответ несколько предназначен для шутки, но на самом деле он работает...

#!/bin/bash
outfile="/tmp/$RANDOM"
cfile="$outfile.c"
echo '#include <stdio.h>
int main(void){int e=1;char c;while((c=getc(stdin))!=-1){if(c==10)e=1;if(c==32)e=0;if(e)putc(c,stdout);}}' >> "$cfile"
gcc -o "$outfile" "$cfile"
rm "$cfile"
cat somedata.txt | "$outfile"
rm "$outfile"

Вы можете заменить cat somedata.txt на другую команду.

Ответ 7

Вы можете попробовать

(whatever) | while read line; do echo $line | head --bytes -3; done;

head сам должен быть быстрее, чем sed или cut, потому что не существует соответствия регулярных выражений или делителей, но при вызове a для каждой отдельной строки, вероятно, перевешивает это.

Ответ 8

Если script всегда выводит строки из 10 символов, за которыми следуют 3 дополнительных (другими словами, вам просто нужны первые 10 символов), вы можете использовать

script | cut -c 1-10

Если он выводит неопределенное количество непространственных символов, за которым следует пробел, а затем еще 2 дополнительных символа (другими словами, вы просто хотите получить первое поле), вы можете использовать

script | cut -d ' ' -f 1

... как в комментарии майхаула ранее. В зависимости от вашей платформы у вас может также быть colrm, который, опять же, будет работать, если строки имеют фиксированную длину:

script | colrm 11

Ответ 9

Другой ответ полагается на третий-последний символ, являющийся пространством. Это будет работать с (почти) любым символом в этой позиции и делает это "БЕЗ использования sed или perl и т.д.":

while read -r line
do
    echo ${line:0:${#line}-3}
done

Если ваши строки фиксированной длины изменяют значение echo на:

echo ${line:0:9}

или

printf "%.10s\n" "$line"

но каждый из них определенно намного медленнее, чем sed.

Ответ 10

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

Я поместил поля, которые у вас были в файл, и сделал это

awk '{ print $1 }' < test.txt 
1234567890
1234567891

Я не знаю, лучше ли это.

Ответ 11

что вы хотите сказать, не хотите использовать sed/awk для скорости? sed/awk быстрее, чем оболочка, в то время как цикл чтения для обработки файлов.

$ sed 's/[ \t]*\*$//' file
1234567890
1234567891

$ sed 's/..\*$//' file
1234567890
1234567891

с bash оболочкой

while read -r a b
do
 echo $a
done <file

Ответ 12

Нет необходимости в разрезе или магии, в bash вы можете вырезать строку так:

  ORGSTRING="123456"
  CUTSTRING=${ORGSTRING:0:-3}
  echo "The original string: $ORGSTRING"
  echo "The new, shorter and faster string: $CUTSTRING"

См. http://tldp.org/LDP/abs/html/string-manipulation.html