Значение настройки командной строки ( PS1) на основе текущей длины строки каталога

#linux #bash #shell #unix #ps1

Вопрос:

Я знаю, что могу сделать это, чтобы отразить в значении только последние 2 каталога PS1 .

 PS1=${PWD#"${PWD%/*/*}/"}#
 

но допустим , у нас есть имя каталога, которое действительно запутано и сократит мое рабочее пространство, например

 T-Mob/2021-07-23--07-48-49_xperia-build-20191119010027#
 

или

 2021-07-23--07-48-49_nokia-build-20191119010027/T-Mob#
 

это последние 2 каталога перед prompt

Я хочу установить условие , если длина каталога любого из последних 2 каталогов превышает пороговое значение, например, 10 символов, сократите имя с 3-мя и 3-мя последними символами каталога (ов), длина которого превышает 10, например

     2021-07-23--07-48-49_xperia-build-20191119010027  amp; 

2021-07-23--07-48-49_nokia-build-20191119010027 
 

оба gt 10 будут сокращены до 202*027 amp; PS1 соответственно

T-Mob/202*027/# for T-Mob/2021-07-23--07-48-49_xperia-build-20191119010027# и 202*027/T-Mob# for 2021-07-23--07-48-49_nokia-build-20191119010027/T-Mob#

Быстрый 1 лайнер, чтобы это сделать ? Я не могу опубликовать это в комментариях, поэтому обновляю здесь. Ссылка на ответ Хоакина ( thx J)

 PS1=''`echo ${PWD#"${PWD%/*/*}/"} | awk -v RS='/' 'length()  <=10{printf $0"/"}; length()>10{printf "%s*%s/", substr($0,1,3),  substr($0,length()-2,3)};'| tr -d "n"; echo "#"`''
 

смотрите ниже o/p

 /root/my-applications/bin # it shortened as expected
my-*ons/bin/#cd -  # going back to prev.
/root
my-*ons/bin/#    #value of prompt is the same but I am in /root
 

Комментарии:

1. Зачем вам однострочник? Разве читабельность (и, следовательно, ремонтопригодность) не важнее краткости?

2.Кроме того, не помечайте свои вопросы знаком awk или sed , если только ваш вопрос не касается awk или sed . Если вы просто думаете, что это инструменты, которые кто-то может использовать для создания ответа, ваш вопрос не об этих инструментах-отметьте unix , если вам нужны ответы, использующие стандартные инструменты UNIX. Тем не менее, для чего-то вроде печати приглашения-это происходит снова и снова с очень небольшим промежутком времени между вызовами-вы не хотите использовать внешние команды (как awk, так и sed) исключительно по соображениям производительности; создание подпроцессов-одна из самых медленных вещей, которые может сделать оболочка.

3. @user1874594: Я предлагаю вам написать функцию, которая создает строку каталога, и использовать эту функцию в своем PS1 . Это проще в отладке и обслуживании.

4. @user1934428, для использования функции в вашей PS1 с помощью подстановки команд требуется разветвление вложенной оболочки. Это медленнее, чем выполнение задания в PROMPT_COMMAND (при условии, конечно, что ваш PROMPT_COMMAND код написан, чтобы избежать вложенных оболочек).

5. @CharlesDuffy: Правильно, но поскольку эта функция выполняется только при выводе приглашения (т. Е. в масштабе времени взаимодействия с пользователем), по крайней мере, создание дочернего процесса-это не то, о чем я бы беспокоился.

Ответ №1:

Однострочный вариант-это в основном всегда неправильный выбор. Пишите код, чтобы он был надежным, читаемым и доступным для обслуживания (и, для того, что часто вызывается или в узком цикле, чтобы быть эффективным), а не быть кратким.

При условии наличия bash 4.3 или более поздней версии:

 # Given a string, a separator, and a max length, shorten any segments that are
# longer than the max length.
shortenLongSegments() {
  local -n destVar=$1; shift  # arg1: where do we write our result?
  local maxLength=$1; shift   # arg2: what's the maximum length?
  local IFS=$1; shift         # arg3: what character do we split into segments on?
  read -r -a allSegments <<<"$1"; shift        # arg4: break into an array

  for segmentIdx in "${!allSegments[@]}"; do   # iterate over array indices
    segment=${allSegments[$segmentIdx]}        # look up value for index
    if (( ${#segment} > maxLength )); then     # value over maxLength chars?
      segment="${segment:0:3}*${segment:${#segment}-3:3}" # build a short version
      allSegments[$segmentIdx]=$segment        # store shortened version in array
    fi
  done
  printf -v destVar '%sn' "${allSegments[*]}" # build result string from array
}

# function to call from PROMPT_COMMAND to actually build a new PS1
buildNewPs1() {
  # declare our locals to avoid polluting global namespace
  local shorterPath
  # but to cache where we last ran, we need a global; be explicit.
  declare -g buildNewPs1_lastDir
  # do nothing if the directory hasn't changed
  [[ $PWD = "$buildNewPs1_lastDir" ]] amp;amp; return 0
  shortenLongSegments shorterPath 10 / "$PWD"
  PS1="${shorterPath}$"
  # update the global tracking where we last ran this code
  buildNewPs1_lastDir=$PWD
}

PROMPT_COMMAND=buildNewPs1 # call buildNewPs1 before rendering the prompt
 

Обратите внимание, что printf -v destVar %s "valueToStore" это используется для записи в переменные на месте, чтобы избежать дополнительных затрат на производительность var=$(someFunction) . Аналогично, мы используем функцию bash 4.3 namevars, доступную с local -n помощью или declare -n , чтобы разрешить параметризацию имен переменных назначения без риска для безопасности eval .


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

 buildNewPs1() {
  local pathPrefix pathFinalSegments
  pathPrefix=${PWD%/*/*}           # everything but the last 2 segments
  pathSuffix=${PWD#"$pathPrefix"}  # only the last 2 segments

  # shorten the last 2 segments, store in a separate variable
  shortenLongSegments pathSuffixShortened 10 / "$pathSuffix"

  # combine the unshortened prefix with the shortened suffix
  PS1="${pathPrefix}${pathSuffixShortened}$"
}
 

…добавление оптимизации производительности, которая перестраивает PS1 только при изменении каталога на эту версию, оставлено в качестве упражнения для читателя.

Комментарии:

1. Спасибо, Чарльз. Вы абсолютно правы с точки зрения практики кодирования — если вас нужно понять, я не могу писать такие вещи, как стены Сфинкса, и навсегда оставляю ppl в догадках. Это замечательно и льется, как жемчужины из бутылки. Но чего бы ни стоил уход за PS1 собой во время работы , я просто хотел посмотреть, можно ли его убрать, вставив несколько дополнительных строк в мою ~/.profile

2. Вы должны быть в состоянии вставить вышесказанное ~/.bashrc (я бы не стал вставлять его .profile по нескольким причинам: .profile также используется оболочками, не относящимися к bash, в то время как вышеописанный код относится к bash, и он вызывается только для оболочек входа, а не для интерактивных оболочек, запускаемых как дочерние элементы этих оболочек входа).

3. да, я была ‘обстрел’, то и часто, но редко обращал ИМП на производительность, потому что если ваше дело с миллионами записей, где МС сэкономленное время добавляет в прямом смысле , его небольшой стоимости, но ваш «Программирование манерами», что делает программирование (супер) человек заслуживают внимания даже в качестве подхода ака шаблон для будущих shell вкладыши и буду иметь в виду

Ответ №2:

Вероятно, не лучшее решение, но быстрое решение с использованием awk:

 PS1=`echo ${PWD#"${PWD%/*/*}/"} | awk -v RS='/' 'length()<=10{printf $0"/"}; length()>10{printf "%s*%s/", substr($0,1,3), substr($0,length()-2,3)};'| tr -d "n"; echo "#"`
 

Я получил эти результаты с вашими примерами:

 T-Mob/202*027/#
202*027/T-Mob/#
 

Комментарии:

1. Смотрите сравнение производительности наших двух ответов на ideone.com/3ykAWR

2. …конечно, это немного несправедливо из-за оптимизации «только повторный запуск, если PWD изменится» в моей, которая закорачивает большую часть кода. ideone.com/TNc2Pr является повторным запуском с этой оптимизацией (благодаря неясностям онлайн-песочниц оба ответа имеют худшую производительность, но общий характер отношений между ними сохраняется-замены команд, конвейеры и запуск новых копий awk -все это дорого).

3. @Joaquin, это динамически меняется, когда вы записываете компакт-диски на другие dir?

4. @pynexj, заключите правую часть назначения в одинарные кавычки, чтобы предотвратить вызов подстановки команды только один раз во время назначения, а затем ее следует оценивать при отображении запроса, а не при выполнении приведенного выше кода.

5. не динамично …. нужно запускать каждый раз. Это то, что я использовал на основе вышеупомянутого fb, описанного в Q