Можно ли отсортировать буквы слова в строке текста?

Можно ли отсортировать буквы слова в строке текста?

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

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цикле и выполняя их после цикла, мы избегаем многократного выполнения дублирующихся опций.

Связанный контент