Bash: извлечь путь пользователя (/home/userID) из строки чтения, содержащей полный путь, и заменить на ~

Я создаю файл сценария bash понемногу. Я учусь на ходу. Но я не могу найти ничего в Интернете, чтобы помочь мне на данный момент: мне нужно извлечь подстроку из большой строки, и два метода, которые я нашел с использованием ${} (фигурные скобки), просто не будут работать.

Первый, ${x#y}, не делает того, что должен.

Второй, ${x:p} или ${x:p:n}, продолжает сообщать о неправильной подстановке.
Кажется, он работает только с константами.

${#x} возвращает длину строки в виде текста, а не числа, что означает, что он не работает ни с ${x:p}, ни с ${x:p:n}.

Дело в том, что заставить bash вообще заниматься математикой очень сложно. За исключением операторов for. Но это только подсчет. И это не задача для цикла for.

Я объединил свой файл сценария здесь, чтобы помочь вам всем понять, что я делаю. Он предназначен для работы с исходными файлами PureBasic, но вам нужно всего лишь изменить аргумент grep "--include=", и вместо этого он сможет искать другие типы текстовых файлов.

#!/bin/bash
home=$(echo ~)                              # Copy the user's path to a variable named home  
len=${#home}                                # Showing how to find the length.  Problem is, this is treated  
                                            # as a string, not a number.  Can't find a way to make over into  
                                            # into a number.  
echo $home "has length of" $len "characters."  
read -p "Find what: " what                  # Intended to search PureBasic (*.pb?) source files for text matches  
grep -rHn $what $home --include="*.pb*" --exclude-dir=".cache" --exclude-dir=".gvfs" > 1.tmp  
while read line                             # this checks for and reads the next line  
do                                          # the closing 'done' has the file to be read appended with "<"  
  a0=$line                                  # this is each line as read  
  a1=$(echo "$a0" | awk -F: '{print $1}')   # this gets the full path before the first ':'  
  echo $a0                                  # Shows full line  
  echo $a1                                  # Shows just full path  
  q1=${line#a1}  
  echo $q1                                  # FAILED!  No reported problem, but failed to extract $a1 from $line.  
  q1=${a0#a1}  
  echo $q1                                  # FAILED!  No reported problem, but failed to extract $a1 from $a0.  
  break                                     # Can't do a 'read -n 1', as it just reads 1 char from the next line.  
                                            # Can't do a pause, because it doesn't exist.  So just run from the  
                                            # terminal so that after break we can see what's on the screen  .
  len=${#a1}                                # Can get the length of $a1, but only as a string  
  # q1=${line:len}                          # Right command, wrong variable   
  # q1=${line:$len}                         # Right command, right variable, but wrong variable type  
  # q1=${line:14}                           # Constants work, but all $home's aren't 14 characters long  
done < 1.tmp  

person oldefoxx    schedule 11.09.2015    source источник
comment
Если комментарии во второй половине вашего сообщения направлены на переполнение стека, то я прошу прощения за ваше разочарование; Я могу отредактировать ваш пост, чтобы он был правильно отформатирован, если хотите. Для многих пользователей впервые размещать сообщения сложнее, чем должно быть, потому что у нас есть очень своеобразные правила для форматирования сообщений и требования к содержанию. Я бы рекомендовал удалить вышеупомянутую часть вопроса, так как это может привлечь больше пользователей, которые захотят ответить.   -  person Shotgun Ninja    schedule 11.09.2015
comment
Обновление: я отредактировал ваш пост и очистил форматирование кода. Если бы я мог сделать что-то еще, чтобы быстро получить ответ на ваш вопрос и развеять ваше разочарование по поводу SO, я бы сделал это.   -  person Shotgun Ninja    schedule 11.09.2015
comment
Ваш комментарий типа строки/числа недействителен. Я не знаю, что вас смущает, но использование len=${#a1}; echo "${line:$len}" работает нормально. Вы можете опустить начальный $ для переменных в арифметическом контексте (что является правой частью : в расширении подстроки), но это работает, только если результатом является число (поэтому ${line#a1} не будет работать).   -  person Etan Reisner    schedule 11.09.2015
comment
Также будьте осторожны с ~, так как он не раскрывается в строках/и т.д. Использование $HOME может быть лучшей идеей (если вам не нужно расширение формата ~user, в этом случае этот ответ (среди прочего) может быть использования.   -  person Etan Reisner    schedule 11.09.2015


Ответы (2)


Следующие работы:

x="/home/user/rest/of/path"
y="~${x#/home/user}"
echo $y

Будет выводить

~/rest/of/path

Если вы хотите использовать «/home/user» внутри переменной, скажем, префикса, вам нужно использовать $ после #, то есть ${x#$prefix}, что, я думаю, является вашей проблемой.

person toth    schedule 11.09.2015

Hejp, который я получил, был высоко оценен. Я сделал это, и вот оно:

#!/bin/bash
len=${#HOME}                                # Showing how to find the length.  Problem is, this is treated
                                            # as a string, not a number.  Can't find a way to make over into
                                            # into a number.
echo $HOME "has length of" $len "characters."
while :
do
  echo
  read -p "Find what: " what                # Intended to search PureBasic (*.pb?) source files for text matches
  a0=""; > 0.tmp; > 1.tmp
  grep -rHn $what $home --include="*.pb*" --exclude-dir=".cache" --exclude-dir=".gvfs" >> 0.tmp
  while read line                           # this checks for and reads the next line
  do                                        # the closing 'done' has the file to be read appended with "<"
    a1=$(echo $line | awk -F: '{print $1}') # this gets the full path before the first ':'
    a2=${line#$a1":"}                       # renove path and first colon from rest of line
    if [[ $a0 != $a1 ]]
    then
      echo  >> 1.tmp
      echo $a1":" >> 1.tmp
      a0=$a1
    fi
    echo " "$a2 >> 1.tmp
  done < 0.tmp
  cat 1.tmp | less
done

Чего у меня пока нет, так это ответа на вопрос, можно ли использовать переменные вместо констант в знаке доллара, фигурных скобках, где вы используете двоеточие, чтобы отметить, что вы хотите вернуть подстроку этой строки, если для этого требуются константы, тогда единственным выбором может быть создание дочернего скрипта с использованием переменных, которые кажутся константами в дочернем элементе, выполнение там, а затем возвращение результатов в переменную среды или временный файл. Я много раз делал подобные вещи с MSDOS. Ограничение здесь заключается в том, что вы должны затем сделать созданный файл исполняемым, используя «chmod + x имя файла». Или назовите его, используя «/bin/bash имя файла».

Другое ограничение bash обнаружило, что вы не можете использовать «sudo» в сценарии, не прервав выполнение текущего сценария. Я предполагаю, что способ обойти это - использовать sudo для вызова /bin/bash для вызова созданного вами дочернего сценария. Тогда я предполагаю, что если дочерний процесс завершится, вы вернетесь к родительскому сценарию, на котором остановились. Если вы не сделали «sudo -i», «sudo -su» или какой-либо другой вариант, в котором вы становитесь суперпользователем. Тогда вам, вероятно, нужно сделать «выход», чтобы удалить оверлей суперпользователя.

Если вы выйдете из дочернего сценария все еще как суперпользователь, наберете ли вы «выход», но вернетесь к завершению родительского сценария? Я подозреваю, что да, что делает некоторые интересные senarios.

Другой вопрос: если вы выполняете строку «во время чтения», что вы можете сделать в bash, чтобы проверить нажатие клавиши на клавиатуре? Опция «чтение» уже используется в этом цикле.

person oldefoxx    schedule 12.09.2015