LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - General (https://www.linuxquestions.org/questions/linux-general-1/)
-   -   Preserve quotes using $* redirected to file. (https://www.linuxquestions.org/questions/linux-general-1/preserve-quotes-using-%24%2A-redirected-to-file-4175679950/)

nicedreams 08-05-2020 10:51 AM

Preserve quotes using $* redirected to file.
 
I'm writing a function for saving a command to a file from a script, but it won't preserve the single and double quotes inside of the command.

It does work from the shell if I do while surrounding it with 'single quotes' (simple example):

> echo 'rsync --exclude="/etc"' >> ~/file.txt

But if I do almost the same thing within a bash script it strips the quotes:

> echo "$*" >> ~/file.txt

I'm not talking about "quoting the beginning and end of entire command" I'm needing all the single/double quotes from within the command arguments because a lot of these commands going forward I want to save are long and complex with quoting.

I've tried creating an array hoping would preserve it all, but same results.

> commands=($*)
> echo ${commands[*]} >> ~/file.txt

Tried with $@ but breaks my command into multiple lines instead of single like if using $*

This is the actual code I'm using from my script:

> printf "%s\n" "$*" >> "${notefile}"

and I've tried using ' %q' within printf, but it adds a bunch of \backslashes\ all over the line of code still missing the single/double quotes.

I've tried '"$*"' (single quotes) since the single quotes work if I do the same thing from the shell itself, but it echo's the actual "$*" into the file and not the command. If I use single quotes around the array '${commands[*]' it puts the actual array ${commands[*]} in the file and not the command itself.

ondoho 08-05-2020 02:27 PM

Frankly, I never use "$*", I always use "$@".
I forget the reasoning, but you should run your script through shellcheck and see what it has to say.

MadeInGermany 08-05-2020 02:59 PM

Quote:

Originally Posted by ondoho (Post 6152982)
Frankly, I never use "$*", I always use "$@".
I forget the reasoning, but you should run your script through shellcheck and see what it has to say.

"$*" converts to one string like "$1 $2 $3"
"$@" preserves the arguments like "$1" "$2" "$3"
With echo you hardly see a difference but with prinf "%s\n" you see it.
Compare with plain arguments like
Code:

echo aa bb cc
echo "aa bb cc"
printf "%s\n" aa bb cc
printf "%s\n" "aa bb cc"

But the o/p problem might be something else. Maybe, when calling the script/function,
in its arguments there is a $variable not wrapped in "quotes"?

nicedreams 08-05-2020 04:55 PM

I'm not using $@ because it will take a long command and make it 9 different lines, but when using $* it stays in one line.

Here is the function to better get an understanding of where the issue may be. Works great except for not preserving quotes. If I edit the .note file and put the quotes in manually on a line I can recall it with no issue so far.

Code:

#!/bin/bash
note() {
  notefile="$HOME/.note"
  if [[ ! -e ${notefile} ]]; then touch ${notefile}; fi
  case "$1" in
    -l|--list|-v|--view)
      cat -n "${notefile}"
      printf "\n"
    ;;
    -c|--clear)
      read -p "Clear contents of ${notefile}? Press Enter to continue or ctrl+c to cancel: " null
      > "${notefile}"
    ;;
    -d|--delete)
      cat -n "${notefile}"
      printf "%s---------------------------------------------\n"
      read -r -p "    Enter line number to remove: " number
      if [[ -z "${number}" ]]; then echo "No input entered"; else sed -i "${number}d" "${notefile}"; fi
    ;;
    -e|--edit)
      ${EDITOR} "${notefile}"
    ;;
    [1-8]|9)
      number="$1"
      line=$(sed -n "${number}"p "${notefile}")
      eval "${line}"
    ;;
    -b|--backup)
      datetime=$(date +%Y-%m-%d_%H.%M.%S)
      cp "${notefile}" "${notefile}"-"${datetime}"; echo "Created backup copy of ${notefile} to ${notefile}-${datetime}"
    ;;
    -h|--help)
      printf "Usage:\n note\t\t\t:print note contents\n note My New Note\t:add new line \"My New Note\" to note\n -l|-v|--list|--view\t:List/View note\n -c|--clear\t\t:Clear entire contents of note file\n -d|--delete\t\t:Delete a line from note\n -e|--edit\t\t:Edit note file with editor\n NUM\t\t\t:Run line number as command\n -b|--backup\t\t:Backup note file with date/time stamp\n -h|--help\t\t:Help\n"
    ;;
    *)
      if [[ -z "$1" ]]; then cat "${notefile}"; else printf "%s\n" "$*" >> "${notefile}"; fi
    ;;
  esac
}

The printf "%s\n" "$*" >> "${notefile}" near the end is my where my issue is.

shellcheck.net didn't have any issues to bring up regarding this.

ondoho 08-06-2020 03:13 AM

Quote:

Originally Posted by nicedreams (Post 6153028)
I'm not using $@ because it will take a long command and make it 9 different lines

Are you saying that printf is the only reason you're not using "$@"? Surely that's an easy fix.
Also I really recommend shellcheck for this - or any - shell script.

michaelk 08-06-2020 07:41 AM

Quotes are interpreted by bash and are not stored in command line arguments or variable values. This is why it works from the shell but not from a script.

You can find several threads on the same problem with lots of suggestions. If your actually trying to pass an entire rsync command what you are trying to do does not sound trivial.

nicedreams 08-06-2020 11:49 AM

This is the line I've been testing since has a lot of quotes redirecting to a file. I'm using printf in function since it's built into bash, but have used echo and get same exact results and will keep using echo in this example.

> echo 'fzf --ansi --header="ctrl-e:Editor ctrl-l:Less enter:bat" --bind "ctrl-e:execute($EDITOR {})" --bind "ctrl-l:execute(less -Rf {})" --bind "enter:execute(bat --color=always {} || less -Rf {})" --preview-window=right:80% --preview "(bat -p --color=always --line-range 1:50 {} || head -50)" --color dark,hl:33,hl+:37,fg+:235,bg+:136,fg+:254 --color info:254,prompt:37,spinner:108,pointer:235,marker:235' >> file.txt

When run from the shell using echo 'command' this works fine and will preserve all the "quotes" in the line redirecting to text file. If I run the same exact command, but within a bash script it doesn't preserve the "quotes" and they are all gone which I understand the reason why, but looking how to get around it. I've tried escaping the single quotes echo \' $* \' but redirects actual $* to file instead of command.

If I use "$@" it will redirect the command to 8 different lines in the text file while not preserving the quotes. When I use "$*" it will put the entire command on one line which I want, but still won't preserve the quotes. Tried not quoting the variables and I get same results.

The point of the function is to be able to add notes or commands to a file and recall it using the line number of the text file. When working on projects and running a lot of commands for testing I want to be able to do

> note !!

and have it put that last command in text file so later I can rerun it easily later like a bookmark doing

> note 1

or whatever line number I want to run. Function works except for preserving quotes which breaks the command when recalling it. If command has not quotes then I can recall it just fine (like htop --help for example).

I've tried print '%q' makes it worse with backslashes all over the command and still no quotes. Tried a loop using $@ to restructure the command to one line, but haven't gotten that working and it still removes the quotes when done inside the script.

shruggy 08-06-2020 12:01 PM

How about ${*@Q}?

rtmistler 08-06-2020 12:27 PM

Yes, as cited $@ is better to use.

Do realize it gives you a list, but you can concatenate that list easily.

Suggest you post some code and results, to be more clear about your problem and continued responses that you feel the standard solutions are not suitable.

It isn't in the best interest of everyone here to incrementally query and contend with each new flavor of this project's challenges. Instead you should give some background and cite technical reasons why there are things blocking you. I would suggest that being open to alternative solutions you may have rejected or not yet tried, may cause you to lose out on opportunities to solve this fairly fast and thus move ahead to your next challenge.

nicedreams 08-06-2020 05:35 PM

Quote:

Originally Posted by shruggy (Post 6153264)
How about ${*@Q}?

I tried that also. Found good detail about it here: https://stackoverflow.com/questions/...uments/1669548

nicedreams 08-06-2020 05:53 PM

Quote:

Originally Posted by rtmistler (Post 6153270)
Yes, as cited $@ is better to use.

Do realize it gives you a list, but you can concatenate that list easily.

Suggest you post some code and results, to be more clear about your problem and continued responses that you feel the standard solutions are not suitable.

It isn't in the best interest of everyone here to incrementally query and contend with each new flavor of this project's challenges. Instead you should give some background and cite technical reasons why there are things blocking you. I would suggest that being open to alternative solutions you may have rejected or not yet tried, may cause you to lose out on opportunities to solve this fairly fast and thus move ahead to your next challenge.


You make it seem like I asked a one line question and never tried to give any other information or try new things. How else you supposed to solve an issue without incremental help?

I'll mark this solved to make you happy.

rtmistler 08-07-2020 12:43 PM

Quote:

Originally Posted by nicedreams (Post 6153335)
You make it seem like I asked a one line question and never tried to give any other information or try new things. How else you supposed to solve an issue without incremental help?

I'll mark this solved to make you happy.

O.K. First, apologies, while I "read" post #4, I fully admit that I committed the bias of reading the first sentence and jumping to conclusions, and then subsequently ignored the code. Once again, apologies.

As noted by Ondoho, this appears to be solely for the print statement. A test script shows that you can use $@, just that the print needs to be broken up somewhat:
Code:

#!/bin/bash

# test code

printf "Using STAR:  %s\n" "$*"
printf "Using AT:  "
printf "%s " "$@"
printf "\n"

Result:
Code:

$ test-print.sh 1 2 3 4 5
Using STAR:  1 2 3 4 5
Using AT:  1 2 3 4 5
$

I'm sure there's ways to fix the print statement so that you might be able to do that all in one line. You also can use a multi line command in an else statement by using brackets.

The arguments to the script are accessible using both of these conventions, and the suggestion was to try and illustrate why or where they were specifically a problem.

Since all that has been shown is that the print statement becomes complicated if you use it there, then it's rather difficult for us to advise how better to craft your script.

Were you attempting to use the $@ somewhere else and did not find that it was suitable for you?

nicedreams 08-07-2020 03:42 PM

Quote:

Originally Posted by rtmistler (Post 6153564)
O.K. First, apologies, while I "read" post #4, I fully admit that I committed the bias of reading the first sentence and jumping to conclusions, and then subsequently ignored the code. Once again, apologies.

As noted by Ondoho, this appears to be solely for the print statement. A test script shows that you can use $@, just that the print needs to be broken up somewhat:
Code:

#!/bin/bash

# test code

printf "Using STAR:  %s\n" "$*"
printf "Using AT:  "
printf "%s " "$@"
printf "\n"

Result:
Code:

$ test-print.sh 1 2 3 4 5
Using STAR:  1 2 3 4 5
Using AT:  1 2 3 4 5
$

I'm sure there's ways to fix the print statement so that you might be able to do that all in one line. You also can use a multi line command in an else statement by using brackets.

The arguments to the script are accessible using both of these conventions, and the suggestion was to try and illustrate why or where they were specifically a problem.

Since all that has been shown is that the print statement becomes complicated if you use it there, then it's rather difficult for us to advise how better to craft your script.

Were you attempting to use the $@ somewhere else and did not find that it was suitable for you?


Thank you I appreciate the apologies.

Using your advice I was able to get $@ to stay on one line (only reason I was using $*) after I removed the \n from the printf statement. Seems it was returning after every quoted break or space with \n which makes sense with $@. Thank you for that.

Still doesn't preserve quotes, but I will use $@ going forward since it's staying one complete line now. Even when $@ was breaking on separate lines it wasn't preserving quotes within the command.

If I use %q

Code:

printf "%q " "$@" >> ~/.note
Then it adds the command to the file (without quotes) and rewrites the command with backslashes properly and when I recall the command from the .note file it works fine so far. The only issue with this is now the command is not easily human readable if I want to copy/paste and modify the command later for something else.

Only time this works properly is if I do it straight from the shell, but when same command is in script, it doesn't preserve quotes.

Code:

printf "%s" 'fzf --ansi --header="ctrl-e:Editor ctrl-l:Less enter:bat" --bind "ctrl-e:execute($EDITOR {})" --bind "ctrl-l:execute(less -Rf {})" --bind "enter:execute(bat --color=always {} || less -Rf {})" --preview-window=right:80% --preview "(bat -p --color=always --line-range 1:50 {} || head -50)" --color dark,hl:33,hl+:37,fg+:235,bg+:136,fg+:254 --color info:254,prompt:37,spinner:108,pointer:235,marker:235' >> ~/.note
I've tried escaping the first and last \'command\' single quotes, but didn't help.

Playing around with older suggestions as maybe those will work now. Will post back later after more testing, but further than where I was before.

Damn \n ruining my entire day. lol

rtmistler 08-07-2020 07:25 PM

Unless someone else understands the shell far better, I do not believe you can get away with this, unless you escape the quotes when calling the script.

Reason being is that the arguments do not enter the script with quotes.

If I'm to understand correctly, you are calling a script and placing single or double quotes around strings, a.k.a. arguments.

Well, another test would be to do the following:
Code:

#!/bin/bash

echo "Using STAR: " $*
echo "Using AT: " $@

These both print out the same thing, and here are the results:
Code:

$ test-print.sh 1 2 3 '4' 5
Using STAR: 1 2 3 4 5
Using AT: 1 2 3 4 5
$
$ test-print.sh 1 2 3 "4" 5
Using STAR: 1 2 3 4 5
Using AT: 1 2 3 4 5
$
$ test-print.sh 1 2 3 \'4\' 5
Using STAR: 1 2 3 '4' 5
Using AT: 1 2 3 '4' 5
$
$ test-print.sh 1 2 3 \"4\" 5
Using STAR: 1 2 3 "4" 5
Using AT: 1 2 3 "4" 5
$

So my contention is that the "shell" from which you invoke your script from is the problem, and it's negating giving the quotes to your script, unless you escape then when you call the script.

nicedreams 08-07-2020 08:05 PM

I'm going to say this is something that cannot be done in bash. I've tried a lot of combinations which give results of the command, but always without quotes no matter the pattern. Only if single quoted from the shell prompt itself.

This is what I've ended up using in the function:

Code:

printf '%q ' "$@" >> "${notefile}"; printf '\n' >> "${notefile}"
and if I want quotes I'll edit (note -e) the file and paste it. Some things I've tried to preserve quotes:

Code:

printf '%s ' ${*@Q} >> "${notefile}"

printf '%q ' "$@" >> "${notefile}"
printf '%q ' $@ >> "${notefile}"

printf '%s ' '"$@"' >> "${notefile}"
printf '%s ' \'"$@"\' >> "${notefile}"

Various:
echo \""$@\"" >> "${notefile}"
echo "''""'"$@"''""'" >> "${notefile}"
echo "\''\"\"'"$@\"\'\'"\"'" >> "${notefile}"

$(echo "$@") >> "${notefile}"
$(echo "$@" >> "${notefile}")



All times are GMT -5. The time now is 10:48 AM.