Как мне разбить чрезвычайно длинный строковый литерал в bash?

#linux #bash

#linux #bash

Вопрос:

Я хотел бы встроить такую длинную команду в сценарий bash:

 mycommand 
    --server myserver 
    --filename extremely/long/file/name/that/i/would/like/to/be/able/to/break/up/if/possible 
    --otherflag 
    --anotherflag
  

с разбитым длинным именем файла.

Я мог бы сделать это:

 # Insufficiently pretty
mycommand 
    --server myserver 
    --filename extremely/long/file/name/
that/i/would/like/to/be/able/to/break/
up/if/possible 
    --otherflag 
    --anotherflag 
  

но это нарушает поток. Я хотел бы иметь возможность написать это:

 # Doesn't work
mycommand 
    --server myserver 
    --filename extremely/long/file/name/
         that/i/would/like/to/be/able/to/break/
         up/if/possible 
    --otherflag 
    --anotherflag
  

но это не работает, потому что это разбивает строковый литерал.

Есть ли способ сообщить bash, чтобы он разбил строковый литерал, но игнорировал любые начальные пробелы?

Ответ №1:

Это немного взломать, но это работает:

 mycommand 
    --server myserver 
    --filename "extremely/long/file/name/"`
               `"that/i/would/like/to/be/able/to/break/"`
               `"up/if/possible" 
    --otherflag 
    --anotherflag
  

Bash объединяет смежные строковые литералы, поэтому мы используем это в своих интересах. Например, echo "hi" "there" печатает hi there , тогда echo "hi""there" как печатает hithere .

Он также использует оператор обратного отсчета и тот факт, что куча пробелов ни к чему не приводит.

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

1. Вы можете поместить начало ` в конец предыдущей строки вместо ​​ продолжения строки… он сохраняет левый край чистым

2. @Peter. O Я не понимаю, о чем вы говорите. Пожалуйста, уточните.

3. грязный хак для очистки вашего кода

4. Я даже не думаю, что вам нужно закрывать и снова открывать двойные кавычки. Это работает для меня: "one` [line break] [whitespace] `two" -> "onetwo"

Ответ №2:

Вы можете использовать переменную :

 file=extremely/long/file/name
file =/that/i/would/like/to/be/able/to/break
file =/up/if/possible

mycommand
    --server myserver
    --filename $file
    --flag flag
  

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

1. Хорошая идея. Вы можете сделать его более чистым, используя = оператор вместо file=${file}/...

2. 1: на мой взгляд, это наименее запутанный подход. Цель ясна.

3. @Chriszuma Да, я забыл, что bash разрешил = оператор. Я отредактировал свой ответ.

4. Найдите некоторое время для решения, но у всех есть небольшие проблемы, такие как добавление дополнительного пространства между. Спасибо

Ответ №3:

Можно также использовать переменную массива

 file=(extremely/long/file/name
    /that/i/would/like/to/be/able/to/break
    /up/if/possible)
IFS=''

echo mycommand
    --server myserver
    --filename "${file[*]}"
    --flag flag
  

Ответ №4:

В принципе, в bash для этого ничего не встроено.
Обычно с оболочкой больше проблем, чем она того стоит, но, тем не менее, вы можете попробовать псевдоним или функцию, например. j

 j(){sed -e ':a;$!N;s/ *n *//g;ta' <<<"$1"}

echo "$(j "3   spaces  
           /hello
           /world
           /this
           /is
           /a
           /long
           /path
          ")"

# 3   spaces/hello/world/this/is/a/long/path
  

Ответ №5:

Я определяю короткую функцию strcat в верхней части моего скрипта bash и использую встроенный вызов, чтобы разделить вещи. Иногда я предпочитаю использовать отдельную переменную, потому что я могу определить длинный литерал в строке с вызовом команды.

 function strcat() {
  local IFS=""
  echo -n "$*"
}

mycommand 
  --server myserver 
  --filename "$(strcat 
      extremely/long/file/name/ 
      that/i/would/like/to/be/able/to/break/ 
      up/if/possible)" 
  --otherflag 
  --anotherflag 
  

Мне также нравится этот подход, когда мне нужно вводить длинный CSV значений в качестве параметра флага, потому что я могу использовать его, чтобы избежать ввода запятой между значениями:

 function strjoin() {
  local IFS="$1"
  shift
  echo -n "$*"
}

csv_args=(
  foo=hello
  bar=world
  "this=arg  has  spaces  in  it"
)
mycommand 
  --server myserver 
  --csv_args "$(strjoin , "${csv_args[@]}")" 
  --otherflag 
  --anotherflag 
  

Что эквивалентно

 mycommand 
  --server myserver 
  --csv_args "foo=hello,bar=world,this=arg  has  spaces  in  it" 
  --otherflag 
  --anotherflag 
  

Ответ №6:

Еще один способ записи длинной строки в переменную с сохранением максимальной длины строки:

 printf -v fname '%s' 
    'extremely/long/file/name/that/i/' 
    'would/like/to/be/able/to/break/up/' 
    'if/possible'
  

Поскольку аргументов больше, чем директив форматирования, %s просто повторяется, и мы получаем

 $ declare -p fname
declare -- fname="extremely/long/file/name/that/i/would/like/to/be/able/to/break/up/if/possible"
  

который можно использовать как

 mycommand 
    --server myserver 
    --filename "$fname" 
    --otherflag 
    --anotherflag
  

Это особенно удобно при настройке длинных переменных с изначально разделенным содержимым, таким как CDPATH (или PATH , конечно):

 printf -v CDPATH '%s' 
    ':/Users/benjamin/here/is/a/long/path' 
    ':/Users/benjamin/and/here/is/another/one' 
    ':/Users/benjamin/and/a/third/line'
export CDPATH
  

в отличие от

 export CDPATH=':/Users/benjamin/here/is/a/long/path:/Users/benjamin/and/here/is/another/one:/Users/benjamin/and/a/third/line'
  

или неуклюжий

 export CDPATH=':/Users/benjamin/here/is/a/long/path'
CDPATH =':/Users/benjamin/and/here/is/another/one'
CDPATH =':/Users/benjamin/and/a/third/line'