make bash accept function parameters - linux

so I have a bash script called myCMD which takes into its argument strings in the form of function()
however when I enter into the command line:
myCMD function()
it would complain -bash: syntax error near unexpected token '('
When I do myCMD "function()" though it will work
however I DON'T want to have to always wrap the argument in quotes everytime I use this command
Is there a way to configure the terminal/my script so that if you just enter myCMD function() it will treat function() as a string and will not complain accordingly even without quotes?

The () must be escaped some how as bash treats them specially otherwise. Quotes will escape as well as \(\). Or you could rename function() to not have () in its name.

In bash "function" is a reserved word, so be careful with that.
https://www.gnu.org/software/bash/manual/html_node/Reserved-Word-Index.html
Why not change myCMD script to do what you want?
You could also wrap myCMD using the read command
#!/bin/bash
read line
myCMD "$line"
Usage requires invoking the wrapper then entering the string:
> myCMDWrapper
function()
myCMD was invoked with function()

Most shells not just bash treats () specially in the open so you have no choice but to quote it: "()", '()', $'()' or \(\).

Related

Bash prompt variables does not work inside a bash function

I'm working on a prompt customization, but for some reason, when I use the \u, \h and \W variables as is it works perfectly, but when I put them inside a function, they are displayed as "\u" or "\W" instead of their values.
...
print_user()
{
echo -e "\001\u\002#\001\h\002"
}
print_dir()
{
echo -e "\001${YELLOW}\002\001\W\002\001${RESET_ATTR}\002"
}
PS1='[$(print_user) on $(print_dir)] $(get_git_repo) \001\n\002$(print_prompt) '
This displays as:
[\u#\h on \W]
>
If I move them outside of the function like so
PS1='[\[\u\]#\[\h\] \[${YELLOW}\]\[\w\]\[${RESET_ATTR}\]] $(get_git_repo) \[\n\]$(print_prompt)'
It works fine, and displays the current directory with the username and hostname:
[myusername#arch on ~]
>
Is this just how bash works? Is there a different way of doing it so it will work? Why is it that inside of a function it won't display the variables' values but outside of a function it does?
From the man page, under PROMPTING
Bash allows these prompt strings to be customized by inserting a number of
backslash-escaped special characters that are decoded as follows:
[...]
After the string is decoded, it is expanded via parameter expansion, command substitution, arithmetic expansion, and quote removal, subject to the value of the
promptvars shell option (see the description of the shopt command under SHELL BUILTIN COMMANDS below).
By the time the shell expands $(print_user) to add \u to the string, it is too late to decode it, so the literal string \u remains in the prompt.
One alternative is to use PROMPT_COMMAND to execute a function that defines PS1 dynamically, just before it is displayed, instead of embedding command substitution in the value of PS1 itself.
make_prompt () {
PS1="[$(print_user) on $(print_dir)] $(get_git_repo)"
PS1+='\[\n\]'
PS1+="$(print_prompt) "
}
PROMPT_COMMAND=make_prompt
Now, print_user will have been called before the shell decodes the value of PS1, by which time all the prompt escapes will be present.

How do I pass ">>" or "<<" to my script without the terminal trying to interpret it as me either appending to something or getting stdin?

My python script can take a series of bitwise operators as one of its arguments. They all work fine except for "=<<" which is roll left, and "=>>" which is roll right. I run my script like ./script.py -b +4,-4,=>>10,=<<1, where anything after -b can be any combination of similar operations. As soon as the terminal sees "<<" though, it just drops the cursor to a new line after the command and asks for more input instead of running the script. When it sees ">>", my script doesn't process the arguments correctly. I know it's because bash uses these characters for a specific purpose, but I'd like to get around it while still using "=>>" and "=<<" in my arguments for my script. Is there any way to do it without enclosing the argument in quotation marks?
Thank you for your help.
You should enclose the parameters that contain special symbols into single quotation marks (here, echo represents your script):
> echo '+4,-4,=>>10,=<<1'
+4,-4,=>>10,=<<1
Alternatively, save the parameters to a file (say, params.txt) and read them from the file onto the command line using the backticks:
> echo `cat params.txt`
+4,-4,=>>10,=<<1
Lastly, you can escape some offending symbols:
> echo +4,-4,=\>\>10,=\<\<1
+4,-4,=>>10,=<<1

converting a string passed from unix shell to a node program to a javascript-formatted one?

I'm using a bash script to send an argument to a node app like so:
testString="\nhello\nthere"
node ./myNodeScript.js $testString
The trouble comes when I use testString inside the node program after capturing it as process.argv[2] -- rather than expand the \n characters to newlines node prints them literally. I need a way to tell node to convert the argument to a javascript string, respecting the formatting characters. Is there a way to go about this?
Try to avoid confusing literal linefeeds and literal backslash followed by literal n.
If you want the string you pass to have linefeeds, you should ignore JavaScript string literal syntax and just pass the linefeeds as linefeeds:
$ cat myNodeScript.js
console.log("Node was passed this: " + process.argv[2])
$ cat myBashScript
testString='
hello
there'
printf 'Bash passes this: %s\n' "$testString"
node myNodeScript.js "$testString"
$ bash myBashScript
Bash passes this:
hello
there
Node was passed this:
hello
there
Arguments should contain data (linefeed) while script files should contain code (quoted linefeed or expanded \n as appropriate in the language). When you make sure not to confuse code and data, you can trivially handle both backslash-en and linefeeds in the same string with no surprises:
testString='
"\nhello\nthere" is JavaScript syntax for:
hello
there'
There are ways to express this on a single line in bash using \n for linefeeds and \\n for backslash-en, you just need to make sure that it remains as code, and doesn't accidentally make it into the variable as data.
Can you try this:
testString=$( printf "\nhello\nthere")
node ./myNodeScript.js "$testString"
And let me know if it works?

'less' the file specified by the output of 'which'

command 'which' shows the link to a command.
command 'less' open the file.
How can I 'less' the file as the output of 'which'?
I don't want to use two commands like below to do it.
=>which script
/file/to/script/fiel
=>less /file/to/script/fiel
This is a use case for command substitution:
less -- "$(which commandname)"
That said, if your shell is bash, consider using type -P instead, which (unlike the external command which) is built into the shell:
less -- "$(type -P commandname)"
Note the quotes: These are important for reliable operation. Without them, the command may not work correctly if the filename contains characters inside IFS (by default, whitespace) or can be evaluated as a glob expression.
The double dashes are likewise there for correctness: Any argument after them is treated as positional (as per POSIX Utility Syntax Guidelines), so even if a filename starting with a dash were to be returned (however unlikely this may be), it ensures that less treats that as a filename rather than as the beginning of a sequence of options or flags.
You may also wish to consider honoring the user's pager selection via the environment variable $PAGER, and using type without -P to look for aliases, shell functions and builtins:
cmdsource() {
local sourcefile
if sourcefile="$(type -P -- "$1")"; then
"${PAGER:-less}" -- "$sourcefile"
else
echo "Unable to find source for $1" >&2
echo "...checking for a shell builtin:" >&2
type -- "$1"
fi
}
This defines a function you can run:
cmdsource commandname
You should be able to just pipe it over, try this:
which script | less

Calling substitute() and expand() inside new command definition in Vim

I'm trying to define a new command in Vim that calls an external script with the name of the current file, but slightly modified. Here's how I defined the command:
:command MyNewCommand !/tmp/myscript.sh substitute(expand("%:p"), "-debug", "", 'g')
In other words, myscript.sh takes one parameter, which is the full pathname of the file being edited, with the string -debug in the pathname removed. My command definition doesn't work because rather than passing the pathname, Vim seems to pass to myscript.sh the entire string itself, beginning with the word substitute. How do I define the command to do what I want? ThanksĀ :).
You can use the system() function to execute the script.
Change the command definition as follows:
:command! MyNewCommand call system('/tmp/myscript.sh ' .
\ shellescape(substitute(expand('%:p'), '-debug', '', 'g')))
To see the output of the command, replace call with echo.
You can use solution similar to #ib's one, but with ! which will show you the output of the shell command (and will also handle case when expand('%:p') contains newlines (it can if FS is fully POSIX-compliant)):
command MyNewCommand execute '!/tmp/myscript.sh' shellescape(substitute(expand('%:p'), '-debug', '', 'g'))
The generic approach is to build a string and then execute it. The problem in your command is that substitute(expand(... is not being evaluated and it's passed as is.
So in a generic example
command MyNewCommand OldCommand expand("%:p")
should be converted to
command MyNewCommand execute 'OldCommand '.expand("%:p")
That way MyNewCommand will just invoke execute with the expression 'OldCommand '.expand("%:p"). execute will evaluate the expression and therefore expand() will get evaluated to the filename and concatenated to 'OldCommand ' resulting in a string of the form 'OldCommand myfilename'. That string then gets executed as an Ex command by the same execute.

Resources