В масштабе всей системы это было бы очень безрассудно по точно причине, которую Вы подозреваете - она вызовет значительную поломку в сценариях запуска и системных утилитах, которые зависят от sh-compatible поведения. Как Ulrich говорит, намного более безопасная альтернатива должна упаковать chroot в ящики или просто установить оболочку по умолчанию всех новых пользователей к/bin/ksh, хотя это не может сделать точно, что Вы хотите.
Это стандартная процедура для awk
awk '
{
k=$2
for (i=3;i<=NF;i++)
k=k " " $i
if (! a[$1])
a[$1]=k
else
a[$1]=a[$1] "<br>" k
}
END{
for (i in a)
print i "\t" a[i]
}' long.text.file
Если файл отсортирован по первому слову в строке, то скрипт может быть более простым
awk '
{
if($1==k)
printf("%s","<br>")
else {
if(NR!=1)
print ""
printf("%s\t",$1)
}
for(i=2;i<NF;i++)
printf("%s ",$i)
printf("%s",$NF)
k=$1
}
END{
print ""
}' long.text.file
или просто bash
unset n
while read -r word definition
do
if [ "$last" = "$word" ]
then
printf "<br>%s" "$definition"
else
if [ "$n" ]
then
echo
else
n=1
fi
printf "%s\t%s" "$word" "$definition"
last="$word"
fi
done < long.text.file
echo
Попробуйте
awk 'BEGIN { before="" }
{ if ( $1 == before ) { $1="" ; printf "<br>%s",$0 ; }
else { printf "\n%s",$0 ;} ; before=$1 ; }
END { printf "\n" ;}'
, которые дают с вашим входом
word1 some text<br> some other text
word2 more text
word3 even more
Tha awk в основном запомните первое слово на предыдущей строке и не печатайте новую строку.
Это действительно стандартный для awk
. Вот краткий раствор, который не меняет операционные данные:
awk 'BEGIN { FS="\t" }
$1!=key { if (key!="") print out ; key=$1 ; out=$0 ; next }
{ out=out"<br>"$2 }
END { print out }'
В Python:
import sys
def join(file_name, join_text):
prefix = None
current_line = ''
for line in open(file_name):
if line and line[-1] == '\n':
line = line[:-1]
try:
first_word, rest = line.split('\t', 1)
except:
first_word = None # empty line or one without tab
rest = line
if first_word == prefix:
current_line += join_text + rest
else:
if current_line:
print current_line
current_line = line
prefix = first_word
if current_line: # do the last line(s)
print current_line
join(sys.argv[2], sys.argv[1])
Это ожидает сепаратор (
) в качестве первого аргумента для программы и имени файла в качестве второго аргумента
perl -p0E 'while(s/^((.+?)\t.*)\n\2\t/$1<br>/gm){}'
(Нужно 2 секунды, чтобы обработать словарь 23MB, 1.5Mlines, в моем 6-ти летнем ноутбуке)
С sed
:
sed '$!N;/^\([^\t]*\t\)\(.*\)\(\n\)\1/!P;s//\3\1\2<br>/;D' <<\IN
word1 some text
word1 some other text
word1 some other other text
word2 more text
word3 even more
word3 and still more
IN
(примечание: для многих sed
s вышеупомянутый переход \ t
недопустим и буквальный
)
И если у вас есть GNU sed
, вы можете написать его немного проще:
sed -E '$!N;/^(\S+\t)(.*)\n\1/!P;s//\n\1\2<br>/;D' <infile
Он работает, постепенно складывая ввод по мере чтения. Если две последовательные строки не начинаются с одной и той же непробельной строки, тогда первая из них будет P
ритирована. В противном случае промежуточная новая строка перемещается в начало строки, а соответствующая строка, следующая сразу за ней (чтобы включить табуляцию) , заменяется строкой
.
Обратите внимание, что используемый здесь метод стекирования может иметь последствия для производительности, если строка, которую собирает sed
, становится очень длинной. Если его размер превышает 8 КБ, он превысит минимальный размер буфера пространства шаблона, указанный в POSIX.
Независимо от того, какая из двух возможностей возникла, последней sed
D
выбирается до первого встречающегося \ n
символа ewline в пространстве шаблонов и начинается с того, что останки. И поэтому, когда две последовательные строки не начинаются с идентичных строк, первая печатается и удаляется, в противном случае выполняется подстановка, и D
elete удаляет только \ n
ewline, который ранее был разделен их.
Итак, приведенная выше команда напечатает:
word1 some text<br>some other text<br>some other other text
word2 more text
word3 even more<br>and still more
Я использовал << \ HERE_DOC
для ввода выше, но вам, вероятно, следует отбросить все из << \ IN
и использовать path / to / infile
вместо этого.