LinuxQuestions.org
Visit Jeremy's Blog.
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - General
User Name
Password
Linux - General This Linux forum is for general Linux questions and discussion.
If it is Linux Related and doesn't seem to fit in any other forum then this is the place.

Notices


Reply
  Search this Thread
Old 08-05-2020, 10:51 AM   #1
nicedreams
Member
 
Registered: Jun 2003
Location: Phoenix, AZ
Posts: 112

Rep: Reputation: 17
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.

Last edited by nicedreams; 08-05-2020 at 10:55 AM. Reason: Add little more detail.
 
Old 08-05-2020, 02:27 PM   #2
ondoho
LQ Addict
 
Registered: Dec 2013
Posts: 15,568
Blog Entries: 9

Rep: Reputation: 4495Reputation: 4495Reputation: 4495Reputation: 4495Reputation: 4495Reputation: 4495Reputation: 4495Reputation: 4495Reputation: 4495Reputation: 4495Reputation: 4495
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.
 
Old 08-05-2020, 02:59 PM   #3
MadeInGermany
Senior Member
 
Registered: Dec 2011
Location: Simplicity
Posts: 1,490

Rep: Reputation: 677Reputation: 677Reputation: 677Reputation: 677Reputation: 677Reputation: 677
Quote:
Originally Posted by ondoho View Post
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"?

Last edited by MadeInGermany; 08-05-2020 at 03:01 PM.
 
Old 08-05-2020, 04:55 PM   #4
nicedreams
Member
 
Registered: Jun 2003
Location: Phoenix, AZ
Posts: 112

Original Poster
Rep: Reputation: 17
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.

Last edited by nicedreams; 08-05-2020 at 05:27 PM.
 
Old 08-06-2020, 03:13 AM   #5
ondoho
LQ Addict
 
Registered: Dec 2013
Posts: 15,568
Blog Entries: 9

Rep: Reputation: 4495Reputation: 4495Reputation: 4495Reputation: 4495Reputation: 4495Reputation: 4495Reputation: 4495Reputation: 4495Reputation: 4495Reputation: 4495Reputation: 4495
Quote:
Originally Posted by nicedreams View Post
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.
 
1 members found this post helpful.
Old 08-06-2020, 07:41 AM   #6
michaelk
Moderator
 
Registered: Aug 2002
Posts: 20,529

Rep: Reputation: 3592Reputation: 3592Reputation: 3592Reputation: 3592Reputation: 3592Reputation: 3592Reputation: 3592Reputation: 3592Reputation: 3592Reputation: 3592Reputation: 3592
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.
 
Old 08-06-2020, 11:49 AM   #7
nicedreams
Member
 
Registered: Jun 2003
Location: Phoenix, AZ
Posts: 112

Original Poster
Rep: Reputation: 17
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.
 
Old 08-06-2020, 12:01 PM   #8
shruggy
Senior Member
 
Registered: Mar 2020
Posts: 1,162

Rep: Reputation: Disabled
How about ${*@Q}?
 
2 members found this post helpful.
Old 08-06-2020, 12:27 PM   #9
rtmistler
Moderator
 
Registered: Mar 2011
Location: USA
Distribution: MINT Debian, Angstrom, SUSE, Ubuntu, Debian
Posts: 8,982
Blog Entries: 13

Rep: Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101
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.
 
Old 08-06-2020, 05:35 PM   #10
nicedreams
Member
 
Registered: Jun 2003
Location: Phoenix, AZ
Posts: 112

Original Poster
Rep: Reputation: 17
Quote:
Originally Posted by shruggy View Post
How about ${*@Q}?
I tried that also. Found good detail about it here: https://stackoverflow.com/questions/...uments/1669548
 
Old 08-06-2020, 05:53 PM   #11
nicedreams
Member
 
Registered: Jun 2003
Location: Phoenix, AZ
Posts: 112

Original Poster
Rep: Reputation: 17
Quote:
Originally Posted by rtmistler View Post
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.

Last edited by nicedreams; 08-06-2020 at 05:54 PM.
 
Old 08-07-2020, 12:43 PM   #12
rtmistler
Moderator
 
Registered: Mar 2011
Location: USA
Distribution: MINT Debian, Angstrom, SUSE, Ubuntu, Debian
Posts: 8,982
Blog Entries: 13

Rep: Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101
Quote:
Originally Posted by nicedreams View Post
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?
 
Old 08-07-2020, 03:42 PM   #13
nicedreams
Member
 
Registered: Jun 2003
Location: Phoenix, AZ
Posts: 112

Original Poster
Rep: Reputation: 17
Quote:
Originally Posted by rtmistler View Post
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
 
Old 08-07-2020, 07:25 PM   #14
rtmistler
Moderator
 
Registered: Mar 2011
Location: USA
Distribution: MINT Debian, Angstrom, SUSE, Ubuntu, Debian
Posts: 8,982
Blog Entries: 13

Rep: Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101Reputation: 4101
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.
 
Old 08-07-2020, 08:05 PM   #15
nicedreams
Member
 
Registered: Jun 2003
Location: Phoenix, AZ
Posts: 112

Original Poster
Rep: Reputation: 17
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}")
 
  


Reply

Tags
quoting, shell


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
[SOLVED] Difference between echo of file within double quotes and without double quotes ankitpandey Programming 2 01-11-2013 09:02 AM
Yet Another Bash Quotes Within Quotes Issue tboyer Linux - Software 17 11-03-2012 11:17 AM
Double Quotes Inside Double Quotes youarefunny Programming 6 06-09-2010 10:21 PM
Problems with quotes and double quotes Andruha Slackware 6 01-02-2010 04:44 PM
Using single quotes vs double quotes in PHP strings vharishankar Programming 6 07-11-2005 11:41 AM

LinuxQuestions.org > Forums > Linux Forums > Linux - General

All times are GMT -5. The time now is 03:10 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration