How to use path with spaces in shell with $* - shell

The following two .sh files are my code.
If I don't use $* everything works, but if I use $* it will prompt "./test2.sh:line 8: C: / Program: No such file or directory".
I use cygwin on windows to run test.sh file.
test.sh
#!/bin/sh
UNITY_PATH="C:/Program Files/Unity/Hub/Editor/2018.4.14f1/Editor/Unity.exe"
BUILD_ANDROID_RES=./test2.sh
${BUILD_ANDROID_RES} \
-UnityPath "${UNITY_PATH}" \
test2.sh with $*
#!/bin/sh
parse_arguments() {
UNITY_PATH=$2
}
build_android_assetbundle() {
echo "aaa ${UNITY_PATH}"
"${UNITY_PATH}" abc
}
parse_arguments $*
build_android_assetbundle
test2.sh without $*
#!/bin/sh
build_android_assetbundle() {
echo "aaa ${UNITY_PATH}"
"${UNITY_PATH}" abc
}
UNITY_PATH=$2
build_android_assetbundle
I hope to be able to use $ *, who can solve this problem? Thank you.

Related

access modifiers in bash

Say I have a bash script and I want some variables to appear when sourced and others to only be accessible from within the script (both functions and variables). What's the convention to achieve this?
Let's say test.sh is your bash script.
What you can do is extract all the common items and put them in common.sh which can be sourced by other scripts.
The BASH_SOURCE array helps you here:
Consider this script, source.sh
#!/bin/bash
if [[ ${BASH_SOURCE[0]} == "$0" ]]; then
# this code is run when the script is _executed_
foo=bar
privFunc() { echo "running as a script"; }
main() {
privFunc
publicFunc
}
fi
# this code is run when script is executed or sourced
answer=42
publicFunc() { echo "Hello, world!"; }
echo "$0 - ${BASH_SOURCE[0]}"
[[ ${BASH_SOURCE[0]} == "$0" ]] && main
Running it:
$ bash source.sh
source.sh - source.sh
running as a script
Hello, world!
Sourcing it:
$ source source.sh
bash - source.sh
$ declare -p answer
declare -- answer="42"
$ declare -p foo
bash: declare: foo: not found
$ publicFunc
Hello, world!
$ privFunc
bash: privFunc: command not found
$ main
bash: main: command not found

bash script: how to "exit" from sourced script, and allow to work non sourced?

I have a script that I'd like people to source, but optionally so. So they can run it with or without sourcing it, it's up to them.
e.g. The following should both work:
$ . test.sh
$ test.sh
The problem is, test.sh contains exit statements if correct args aren't passed in. If someone sources the script, then the exit commands exit the terminal!
I've done a bit of research and see from this StackOverflow post that I could detect if it's being sourced, and do something different, but what would that something different be?
The normal way to exit from a sourced script is simply to return (optionally adding the desired exit code) outside of any function. Assuming that when run as a command we have the -e flag set, this will also exit from a shell program:
#!/bin/sh -eu
if [ $# = 0 ]
then
echo "Usage $0 <argument>" >&2
return 1
fi
If we're running without -e, we might be able to return || exit instead.
There may be better ways to do this, but here's a sample script showing how I got this to work:
bparks#home
$ set | grep TESTVAR
bparks#home
$ ./test.sh
Outputs some useful information to the console. Please pass one arg.
bparks#home
$ set | grep TESTVAR
bparks#home
$ . ./test.sh
Outputs some useful information to the console. Please pass one arg.
bparks#home
$ set | grep TESTVAR
bparks#home
$ ./test.sh asdf
export TESTVAR=me
bparks#home
$ set | grep TESTVAR
bparks#home
$ . ./test.sh asdf
bparks#home
$ set | grep TESTVAR
TESTVAR=me
bparks#home
$
test.sh
#!/usr/bin/env bash
# store if we're sourced or not in a variable
(return 0 2>/dev/null) && SOURCED=1 || SOURCED=0
exitIfNotSourced(){
[[ "$SOURCED" != "0" ]] || exit;
}
showHelp(){
IT=$(cat <<EOF
Outputs some useful information to the console. Please pass one arg.
EOF
)
echo "$IT"
}
# Show help if no args supplied - works if sourced or not sourced
if [ -z "$1" ]
then
showHelp
exitIfNotSourced;
return;
fi
# your main script follows
# this sample shows exporting a variable if sourced,
# and outputting this to stdout if not sourced
if [ "$SOURCED" == "1" ]
then
export TESTVAR=me
else
echo "export TESTVAR=me"
fi
Checkout this answer for better description and porper solution.
And here is how it is used in docker-entrypoint.sh in official Mysql image:
# check to see if this file is being run or sourced from another script
_is_sourced() {
# https://unix.stackexchange.com/a/215279
[ "${#FUNCNAME[#]}" -ge 2 ] \
&& [ "${FUNCNAME[0]}" = '_is_sourced' ] \
&& [ "${FUNCNAME[1]}" = 'source' ]
}

Bash function comment extraction from sourced file

I have a bash script with functions I have sourced from a random file, that I no longer retain the original path.
#!/bin/bash
my_awesome_function()
{
#- Usage: my_awesome_function <key> <to> <success>
echo "I'm doing something great."
}
declare -x -f my_awesome_function
I have previously ran:
$ source ./some_random_file_i_dont_know_where_it_is
And followed up with (in the same shell):
$ type my_awesome_function
my_awesome_function is a function
my_awesome_function ()
{
echo "I'm doing something great."
}
I'm asking because I would like to include automated usage for this function. In the ideal case:
exceptional_help()
{
echo ; type $1 | grep "#-" | tr -d "#-"
}
Which could be used like follows:
$ exceptional_help "my_awesome_function"
Usage: my_awesome_function <key> <to> <success>
I have tried type and declare and which and a few other builtins, but nothing seems to retain the original formatting including comments, or a reference to the original function that I could then parse again.
Try:
my_awesome_function() {
[ "$1" = "--help" ] && {
echo 'Usage: my_awesome_function <key> <to> <success>'
return
}
echo "I'm doing something great."
}
Example:
$ my_awesome_function
I'm doing something great.
$ my_awesome_function --help
Usage: my_awesome_function <key> <to> <success>
In practice, it would be much more reliable to follow #John1024's solution. In its entirety if you can. I wrote a small wrapper around the source functionality that will extract specific comments for docs. That borrows the easy way to write a help string.
For future reference here is a macro orientated approach.
I couldn't quite get sourcing to work from stdin on BSD and bash 4, but I'm sure it is achievable without an intermediary file.
It also did need an additional replacement to close the brackets and this doesn't play nice with multi-line usage strings.
Random file:
my_awesome_function(){
#--Usage : hello <arg1> <arg2> ++#
echo "I'm doing something great."
echo "and I'm totally self documented."
}
Custom sourcing :
self_documenting_source(){
local src tmpfile
src=$1
tmpfile=$(mktemp /tmp/source.XXXXXX)
< $src \
sed 's/#--/[ "$1" == "--help" ] \&\& { echo \" /' \
| sed 's/#++/\"; return; }/' \
> $tmpfile
source $tmpfile
rm $tmpfile
}
(As a side note, turns out the & represents the input substitution in BSD sed.)
Results:
$ my_function --help
Usage : hello <arg1> <arg2>
$ my_function
I'm doing something great.
and I'm totally self documented.

Generate multiline file with Make

I want to create a multiline file with Make, having exact content:
#!/bin/bash
if [ "$JAVA_HOME" = "" ]; then echo "Please set JAVA_HOME"; exit 1; fi
export CONFIG_VARS=$( cat <<EOF
-Dmapred.job.tracker=$JT
EOF
)
${HADOOP_HOME}/bin/hadoop $1 $HADOOP_CONFIG_VARS ${*:2} 2>&1 | grep -v SLF4J
How can I tell make to output a file with this exact content somewhere?
I tried this:
define SCRIPT_CONTENT
#!/bin/bash
if [ "$JAVA_HOME" = "" ]; then echo "Please set JAVA_HOME"; exit 1; fi
export CONFIG_VARS=$( cat <<EOF
-Dmapred.job.tracker=$JT
EOF
)
${HADOOP_HOME}/bin/hadoop $1 $HADOOP_CONFIG_VARS ${*:2} 2>&1 | grep -v SLF4J
endef
export SCRIPT_CONTENT
bin/script:
#echo "$$SCRIPT_CONTENT" > bin/script
This paricular solution 1) wipes $ and first char after $-es and 2) is ugly because the definition should happen outside of the particular target where it's needed :(
I also tried this:
bin/script:
#echo '
#!/bin/bash
if [ "$JAVA_HOME" = "" ]; then echo "Please set JAVA_HOME"; exit 1; fi
export CONFIG_VARS=$( cat <<EOF
-Dmapred.job.tracker=$JT
EOF
)
${HADOOP_HOME}/bin/hadoop $1 $HADOOP_CONFIG_VARS ${*:2} 2>&1 | grep -v SLF4J
' > bin/script
This returns error when in make, works outside of make...
Any suggestion is very welcome!
Make wants any $ characters that should be reproduced literally to be escaped by inserting another $ in front of them.
More broadly, though, it seems like you're trying to use Make as a shell-script replacement. The more idomatic way to do this would be to put that content in a source file that you can copy to the destination, or to put it in a script that will write it into a specified destination. The Makefile then just has to invoke the copy command or the script.
With the help from this magnificent answer, I cooked up the following.
# From https://stackoverflow.com/a/8316519/874188
define \n
endef
define SCRIPT_CONTENT
#!/bin/bash
if [ "$$JAVA_HOME" = "" ]; then echo "Please set JAVA_HOME"; exit 1; fi
export CONFIG_VARS=$$( cat <<EOF
-Dmapred.job.tracker=$$JT
EOF
)
$${HADOOP_HOME}/bin/hadoop $$1 $$HADOOP_CONFIG_VARS $${*:2} 2>&1 | grep -v SLF4J
endef
bin/script:
echo '$(subst $(\n),\n,$(SCRIPT_CONTENT))' >$#
When testing, I found that I needed to have a semicolon at the end of the echo line when it didn't have any redirection. I can speculate that there is a built-in echo which gets invoked when there are no shell metacharacters in the command line?
Also, notice that the definition cannot contain any single quotes, and that all dollar signs have to be doubled. Maybe one or the other of these restrictions could be removed; I was unsuccessful, but then I didn't spend too much time or effort.
You cannot do this in make. Beyond what Novelocrat says regarding $, there's the fact that make is line-oriented and does not have any ability to generate a command that contains a newline character. All newlines that appear unescaped (without a backslash before them) are parsed by make as ending that recipe line, and each recipe line is sent to a different invocation of the shell. If you want the entire command to be sent as a single string to the same shell, then you must escape the newlines.
However, make will remove all backslash/newline pairs before it runs the command.
The only possible way to do this completely within make is to generate the file one line at a time, like this:
bin/script:
#echo '#!/bin/bash' > $#
#echo 'if [ "$$JAVA_HOME" = "" ]; then echo "Please set JAVA_HOME"; exit 1; fi' >> $#
#echo 'export CONFIG_VARS=$$( cat <<EOF' >> $#
#echo ' -Dmapred.job.tracker=$$JT' >> $#
#echo 'EOF' >> $#
#echo ')' >> $#
#echo '$${HADOOP_HOME}/bin/hadoop $$1 $$HADOOP_CONFIG_VARS $${*:2} 2>&1 | grep -v SLF4J' >> $#
As Novelocrat says, the typical way this is done is to have the script file as a separate file and copy it where you want it, rather than generating it.

How do i populate this variable in bash?

I am trying to run something like this from a bash script:
HOST=foo
DIR=bar
ssh user#$HOST "
function test
{
CURHOST=$HOST
cd $DIR
mkdir -p $CURHOST
}; test"
When I run it with set -x I see that it translates to this:
+ ssh user#foo '
function test
{
CURHOST=foo
cd bar
mkdir -p
}; test
and then of course it complains about mkdir -p having no argument. Why is this and how can I set a local variable in there?
To prevent substitution, you can use either single-quotes '...' or a backslash \; for example, this command:
echo '$foo' \$foo "\$foo"
will print this:
$foo $foo $foo
and will not use the variable foo.
In your case, rather than using "..." for the whole argument, you probably should use '...' everywhere except where you specifically need substitution. So:
HOST=foo
DIR=bar
ssh user#$HOST '
function test
{
CURHOST='"$HOST"'
cd $DIR
mkdir -p $CURHOST
}; test'
Just escape the dollar sign:
echo "
function test
{
CURHOST=$HOST
cd $DIR
mkdir -p \$CURHOST
}; test"
gives you:
function test
{
CURHOST=foo
cd bar
mkdir -p $CURHOST
}; test

Resources