
Итак, у меня есть файл, полный тестовых команд, которые я люблю запускать против некоторых своих функций, чтобы убедиться, что они правильно обрабатывают все возможные ситуации. Хотя нет смысла иметь дублирующие команды. Вот несколько примеров:
rap ,Xflg MIT X11
rap ,XPBfl 'MITER'
rap ,Bflg share git-grep
rap ,bfl X11
rap ,Bfl xzfgrep
rap ,Bf X11
... моя функция 'rap' использует запятую вместо тире для указания начальных буквенных опций, затем следует некоторый аргумент. Поскольку порядок этих опций не имеет значения:
rap ,Bf X11
rap ,fB X11
... — это абсолютно та же команда. Конечно, легко удалить дубликаты строк из файла, однако, чтобы избежать указанной выше проблемы, я бы хотел иметь возможность сортировать параметры в алфавитном порядке, чтобы в итоге получилось:
rap ,Bf X11
rap ,Bf X11
... и тогда я смогу удалить дубликаты. Можно ли сделать что-то подобное без геройства? Обратите внимание, что это не сортировка «по» списку опций, а сортировка самих опций.
решение1
Другой perl
вариант:
$ perl -pe 's{^rap ,\K\S+}{join "", sort split //, $&}e' file
rap ,Xfgl MIT X11
rap ,BPXfl 'MITER'
rap ,Bfgl share git-grep
rap ,bfl X11
rap ,Bfl xzfgrep
rap ,Bf X11
Что касается вашего дополнительного требования, чтобы строчные буквы располагались перед заглавными, вы можете положиться на тот факт, что в ASCII 'x'
это 'X' ^ 32
(и 'X'
это 'x' ^ 32
):
$ perl -pe 's{^rap ,\K\S+}{join "", sort {(ord($a)^32) <=> (ord($b)^32)} split //, $&}e' file
rap ,fglX MIT X11
rap ,flBPX 'MITER'
rap ,fglB share git-grep
rap ,bfl X11
rap ,flB xzfgrep
rap ,fB X11
решение2
Вы можете использовать Perl для захвата последовательности символов слова, следующих за запятой, разбить результат на массив, отсортировать его и подставить результат:
$ perl -pe 's{(?<=,)(\w+)}{join "", sort split(//, $1)}e' yourfile
rap ,Xfgl MIT X11
rap ,BPXfl 'MITER'
rap ,Bfgl share git-grep
rap ,bfl X11
rap ,Bfl xzfgrep
rap ,Bf X11
По вашей просьбе вот один (вероятно, не самый оптимальный) способ отсортировать все варианты со строчными буквами перед всеми вариантами с заглавными:
$ perl -pe 's{(?<=,)(\w+)}{@opts = split(//,$1); join "",
(sort grep /[[:lower:]]/,@opts), (sort grep /[^[:lower:]]/, @opts)
}e' yourfile
rap ,fglX MIT X11
rap ,flBPX 'MITER'
rap ,fglB share git-grep
rap ,bfl X11
rap ,flB xzfgrep
rap ,fB X11
решение3
Использование GNU awk дляsorted_in
и, поскольку мы все равно используем gawk, несколько других удобных, но ненужных расширений, мы можем применитьИдиома «Украсить-Сортировать-Неукрасить»поместив 1
перед всеми строчными символами и 2
перед заглавными, чтобы заставить все строчные символы сортироваться перед заглавными, а затем снова удалить эти украшения перед печатью:
$ cat tst.awk
BEGIN { PROCINFO["sorted_in"] = "@val_str_asc" }
match( $0, /^(\s*\S+\s*,)(\S+)(.*)/, a ) {
gsub( /[[:lower:]]/, "1 &,", a[2] ) # Decorate
gsub( /[[:upper:]]/, "2 &,", a[2] )
sorted = ""
split(a[2],opts,",")
for ( idx in opts ) { # Sort
sorted = sorted opts[idx]
}
gsub( /[[:digit:] ,]/, "", sorted ) # Undecorate
$0 = a[1] sorted a[3]
}
{ print }
$ awk -f tst.awk file
rap ,fglX MIT X11
rap ,flBPX 'MITER'
rap ,fglB share git-grep
rap ,bfl X11
rap ,flB xzfgrep
rap ,fB X11
решение4
Если заменить запятую во входном файле на тире, то можно будет использовать ее getopts
как обычно для анализа rap
параметров функции.
Это изменение можно сделать с помощью sed
, и если предположить, что нам нужно изменить только rap ,
начало любой строки на rap -
, то это будет выглядеть так:
sed 's/^rap ,/rap -/' file.in >file
Затем мы могли бы просто указать сгенерированный файл в нашем скрипте, предполагая, что . ./file
функция rap
была ранее объявлена.
Для анализа опций в rap
функции:
rap () {
OPTIND=1
unset -v B_flag P_flag X_flag
unset -v b_flag f_flag g_flag l_flag
while getopts BPXbfgl opt; do
case $opt in
B) B_flag=true ;;
P) P_flag=true ;;
X) X_flag=true ;;
b) b_flag=true ;;
f) f_flag=true ;;
g) g_flag=true ;;
l) l_flag=true ;;
*) echo 'Error' >&2; return 1
esac
done
shift "$(( OPTIND - 1 ))"
# Act on set flags here.
if "${f_flag-false}"; then
echo 'The -f option was used'
fi
# The non-options are available in "$@".
printf 'Other argument: %s\n' "$@"
printf -- '---\n'
}
Обратите внимание, что, устанавливая переменные-флаги в while
цикле и выполняя их после цикла, мы избегаем многократного выполнения дублирующихся опций.