Shell variable expansion: Storing multiple command arguments that include spaces in one variable - sh

I'm writing a shell script to drive xcodebuild, the Xcode command line frontend.
xcodebuild takes build settings on the command line in the following manner:
$ xcodebuild a=value b=value
value may contain spaces, for example it could be a list of paths.
So I would invoke it like so:
$ xcodebuild "a=value1 value2 value3" "b=value4 value5"
xcodebuild will report the settings as expected:
Build settings from command line:
a = value1 value2 value3
b = value4 value5
However when invoked as follow:
$ xcodebuild "a=value1 b=value2"
This will be interpreted as a single argument, assigning a value to build setting a:
Build settings from command line:
a = value1 b=value2
Now, the real question is I want to write a shell script that will have all the build settings in one variable, e.g. I can do this:
SETTING_A="a=value1 value2 value3"
SETTING_B="b=value4 value5"
xcodebuild "${SETTING_A}" "${SETTING_B}"
However that does not scale.
And I want to do something like this:
SETTING_A="a=value1 value2 value3"
SETTING_B="b=value4 value5"
SETTINGS=${SETTING_A} ${SETTING_B}
xcodebuild ${SETTINGS}
However the above doesn't work and no matter what variations I tried, I can't keep SETTING_A and SETTING_B as two separate words/arguments for the command. It's either 1 if I use "$SETTINGS" or 5 if I use just $SETTINGS.
I'm looking for standard unix shell solutions but will entertain Bash or Zsh specific one if this is not doable in standard sh.

Using standard sh (POSIX), just use set on the individual setting expansions and call "$#" to pass them, i.e.
SETTING_A="a=value1 value2 value3"
SETTING_B="b=value4 value5"
set -- "${SETTING_A}" "${SETTING_B}"
xcodebuild "$#"

Related

"log=..." command-line parameter to send script output to STDOUT? [duplicate]

I'm working with a command line utility that requires passing the name of a file to write output to, e.g.
foo -o output.txt
The only thing it writes to stdout is a message that indicates that it ran successfully. I'd like to be able to pipe everything that is written to output.txt to another command line utility. My motivation is that output.txt will end up being a 40 GB file that I don't need to keep, and I'd rather pipe the streams than work on massive files in a stepwise manner.
Is there any way in this scenario to pipe the real output (i.e. output.txt) to another command? Can I somehow magically pass stdout as the file argument?
Solution 1: Using process substitution
The most convenient way of doing this is by using process substitution. In bash the syntax looks as follows:
foo -o >(other_command)
(Note that this is a bashism. There's similar solutions for other shells, but bottom line is that it's not portable.)
Solution 2: Using named pipes explicitly
You can do the above explicitly / manually as follows:
Create a named pipe using the mkfifo command.
mkfifo my_buf
Launch your other command with that file as input
other_command < my_buf
Execute foo and let it write it's output to my_buf
foo -o my_buf
Solution 3: Using /dev/stdout
You can also use the device file /dev/stdout as follows
foo -o /dev/stdout | other_command
Named pipes work fine, but you have a nicer, more direct syntax available via bash process substitution that has the added benefit of not using a permanent named pipe that must later be deleted (process substitution uses temporary named pipes behind the scenes):
foo -o >(other command)
Also, should you want to pipe the output to your command and also save the output to a file, you can do this:
foo -o >(tee output.txt) | other command
For the sake of making stackoverflow happy let me write a long enough sentence because my proposed solution is only 18 characters long instead of the required 30+
foo -o /dev/stdout
You could use the magic of UNIX and create a named pipe :)
Create the pipe
$ mknod -p mypipe
Start the process that reads from the pipe
$ second-process < mypipe
Start the process, that writes into the pipe
$ foo -o mypipe
foo -o <(cat)
if for some reason you don't have permission to write to /dev/stdout
I use /dev/tty as the output filename, equivalent to using /dev/nul/ when you want to output nothing at all. Then | and you are done.

Generate Swift Interface from Objective-C Header from Command Line

In Xcode, for any Objective-C header, we can view the Generated Interface, which shows how it is seen by Swift in interop.
I'd like to generate that from the command line. Any idea how to do it?
Bonus task: The header should be precompiled first, so all #imports should be replaced already.
Invoke interpreter command :type lookup on the module you are trying to inspect.
Suppose you have a header file named header.h. Put it into a separate directory, so that the interpreter would recognise it as a module. Also create a modulemap in the same directory. Let's call this directory Mod:
./
./Mod/
/header.h
/module.modulemap
Fill in the modulemap with the following:
module Mod {
header "./header.h"
export *
}
Once it's done, issue a command like this:
echo "import Mod\n:type lookup Mod" | swift -I./Mod | tail -n+2 >| generated-interface.swift
Alternatively, you might want use a command like this with equal effect:
echo "import Mod\n:print_module Mod" | swift -deprecated-integrated-repl -I./Mod >| generated-interface.swift
It's broken down as follows:
first we echo the script to be executed: import module and type-lookup it;
then we launch the interpreter and feed the script into it; the -I argument helps it find our module, which is crucial;
then we cut off the “Welcome to Swift” part with Tail
and write the result into generated-interface.swift.
While running the above commands, make sure your working directory is set to one level higher than the Mod directory.
Note that the output might not be exactly the same as from Xcode, but it's very similar.
Just for the record, if you want to produce the interface from a Swift file, then it's just this:
swiftc -print-ast file.swift

How to empty a file in fish?

In bash shell scripting, I would typically run :> file to empty a file.
Now using fish, things are slightly different and the above command doesn't work.
What is fish equivalent?
Although it's not as short as :, true is a command that will work everywhere and produces no output:
true > file
Probably the easiest way that will be work in both Fish and Bash is to do echo "" > file
EDIT: Commenter was absolutely right echo "" > file produces a file with a newline, the correct command I was thinking of to create an empty file is cat /dev/null > file.
There is, and always was the magic method called touch which set change time to actual or create non-existent file. For compatiblity I suggest you to use this way in all scripts that you write (even if you write bash code).

Read environment variable using Macros iOS [duplicate]

In the code below, I would like the value of THE_VERSION_STRING to be taken from the value of the environment variable MY_VERSION at compile time
namespace myPluginStrings {
const char* pluginVendor = "me";
const char* pluginRequires = THE_VERSION_STRING;
};
So that if I type:
export MY_VERSION="2010.4"
pluginRequires will be set at "2010.4", even if MY_VERSION is set to something else at run time.
UPDATE: (feb 21) Thanks for your help everyone. It works.
As I'm using Rake as a build system, each of my CFLAGS is a ruby variable. Also the values need to end up in quotes. Therefore the gcc command line for me needs to look like this:
gcc file.c -o file -D"PLUGIN_VERSION=\"6.5\""
Which means this is in my Rakefile:
"-D\"PLUGIN_VERSION=\\\"#{ENV['MY_VERSION']}\\\"\""
If I recall correctly, you can use the command line parameter -D with gcc to #define a value at compile time.
i.e.:
$ gcc file.c -o file -D"THE_VERSION_STRING=${THE_VERSION_STRING}"
In the code below, I would like the value of THE_VERSION_STRING to be taken from the value of the environment variable MY_VERSION at compile time
No, you can't do it like this. The only way to extract environment variables is at runtime with the getenv() function. You will need to explicitly extract the value and copy it to pluginRequires.
If you want the effect of a compile-time constant, then you'll have to specify the definition on the compiler commandline as Seth suggests.

Powershell variable expansion when calling other programs

I have a small problem trying to unzip a file using the 7za
command-line utility in Powershell.
I set the $zip_source variable to the zip file's path and the
$unzip_destination to the desired output folder.
However the command-line usage of 7za needs arguments specified like this:
7za x -y <zip_file> -o<output_directory>
So my current call looks like this:
& '7za' x -y "$zip_source" -o$unzip_destination
Due to the fact that there can be no space between -o and the destination it
seems that PowerShell will not expand the $unzip_destination variable, whereas $zip_source is expanded.
Currently the program simply extracts all the files into the root of C:\ in
a folder named $unzip_destination.
Setting different types of quotes around the variable won't work neither:
-o"$unzip_destination" : still extracts to C:\$unzip_destination
-o'$unzip_destination' : still extracts to C:\$unzip_destination
-o $unzip_destination : Error: Incorrect command line
Is there any way to force an expansion before running the command?
Try this:
& '7za' x -y "$zip_source" "-o$unzip_destination"
try like this:
-o $($unzip_destination)
Editor's note: This solution only works with a space after -o (in which case just -o $unzip_destination would do) - if you remove it, the command doesn't work as intended.
This approach is therefore not suitable for appending a variable value directly to an option name, as required by the OP.
This should work:
& '7za' x -y $zip_source -o${unzip_destination}