-1

In my script I want to proceed first argument as necessary file name and all other arguments as shell commands separated by && (and operator).

echo "${@:2}"

But Bash cuts all commands except first one.

How to fix it?

#!/usr/bin/env bash
while inotifywait -q -e close_write -e delete $1; do "${@:2}"; du app; done;
./watch . g++ app.cpp -o app && time ./app && du app
nkjt
  • 7,825
  • 9
  • 22
  • 28
Alex Ushakov
  • 2,139
  • 2
  • 11
  • 8
  • Please make an example and put some more code. – Mathias Begert Aug 18 '15 at 06:55
  • What?! Could you give an example? – Biffen Aug 18 '15 at 06:56
  • while inotifywait -q -e close_write -e delete $1; do "${@:2}"; du app; done; ./watch . g++ app.cpp -o app && time ./app && du app – Alex Ushakov Aug 18 '15 at 06:59
  • @AlexUshakov And what is the problem, exactly? What do you expect to happen, and what happens? – Biffen Aug 18 '15 at 07:00
  • What you need to understand in bash is `$0` (script file name), `$1` (first arg), `$2` (second arg)... When you use `${@:2}` you are using all arguments beginning with `$2` (e.g. `$2`, `$3`, ...). That is how bash works. I am still unclear on what your goal is in your `while inotifywait...` line. Also, what is the numerical `return` from `inotifywait`? If it is any positive number, the `while` loop will exit. (`0` - true, `#` - false in bash) – David C. Rankin Aug 18 '15 at 07:05
  • Script must compile, show execution time and ready binary size. But it only compiles. – Alex Ushakov Aug 18 '15 at 07:05
  • @AlexUshakov You do know that you're only executing your script with five arguments (`.`, `g++`, `app.cpp`, `-o` and `app`), right? The rest of that line is handled by Bash, and will be executed *after* the script. – Biffen Aug 18 '15 at 07:07
  • There is nothing wrong with `g++ app.cpp -o app && time ./app && du app`. That should be working fine. (I'll have to lookup what `watch` contributes. How exactly are you calling your script? – David C. Rankin Aug 18 '15 at 07:10
  • Is there any way to avoid problem? Maybe I should use getopt? – Alex Ushakov Aug 18 '15 at 07:11
  • @DavidC.Rankin `./watch . g++ app.cpp -o app && time ./app && du app` or `./watch . "g++ app.cpp -o app && time ./app && du app"` – Alex Ushakov Aug 18 '15 at 07:12
  • 1
    @AlexUshakov What? What *is* ‘the problem’? – Biffen Aug 18 '15 at 07:12
  • I use inotifywait utility for looking on abstract sources directory. When it detects any change it must do several things. For example, compile and launch, etc. The question is - how to make script (it will be stored in directory in $PATH) that will look at necessary directory (first agrument) and do necessary things (all other arguements) separated by &&. – Alex Ushakov Aug 18 '15 at 07:17
  • @AlexUshakov If you want to send `&&` to the script you'll have to quote it. – Biffen Aug 18 '15 at 07:29
  • https://asciinema.org/a/4akup5zog5xh00rkys9ot53m7 – Alex Ushakov Aug 18 '15 at 07:40
  • this dont work `./watch . command0 && command1` – Alex Ushakov Aug 18 '15 at 07:41
  • @AlexUshakov Of course it doesn't. The *unqouted* `&&` is parsed and used by Bash. – Biffen Aug 18 '15 at 07:44
  • My mistake. I mean `./watch . "command0 && command1"` – Alex Ushakov Aug 18 '15 at 07:44
  • @AlexUshakov As for the film, it looks like you're trying to execute the rest of the arguments as a single command, since you quote `${@:2}`. Hence the ‘`g++ app.cpp -o app && time ./app && du app: No such file or directory`’. Remove the quotes or use `eval` or something. But [be careful](http://mywiki.wooledge.org/BashFAQ/050). – Biffen Aug 18 '15 at 07:45

3 Answers3

0

Alex, I get the jest of what you are attempting to do with inotifywait, but when I attempted it, it would just hang and do nothing as the files were static at the point it was called. Looking at what you are attempting to accomplish. (build, run, measure disk usage of app) it may be easier to just script it instead of putting inotifywait in the middle of it.

While this is just a basic hack at getting the output you wanted. The script below will take the source file as the first argument (app.cpp by default if no name given) and will compile the code using the name before .cpp as the g++ output file name. It is a simple attempt to get the information you were looking for:

#!/bin/bash

src=${1:-app.cpp}   ## set the source and output (exe) names
out=${src%.cpp}

printf "\n compiling 'g++ %s -o %s'\n\n" $src $out

[ -f $out ] && rm $out      ## remove existing exe before builinding

g++ $src -o $out            ## build new exe

[ -f $out ] || {            ## validate new exe created
    printf "error: compilation failed. exiting script.\n\n"
    printf "usage:   %s source.cpp (default: app.cpp)\n\n"
    exit 1
}

[ -e $out ] || {            ## validate it is executable
    printf "error: file produced from compilation is not executable.\n"
    exit 1
}

time ./$out 2>&1                        ## compute elapsed time
exesz=$(du -h $out | awk '{print $1}')  ## store disk usage (removing name)

## print results
printf "\n Source file:  %s\n" "$src"
printf " output file:  %s\n" "$out"
printf " size of exe:  %s bytes\n\n" "$exesz"

exit 0

Example Output

$ ./watch.sh

 compiling 'g++ app.cpp -o app'

Code Running
and being timed.

real    0m0.004s
user    0m0.001s
sys     0m0.002s

 Source file:  app.cpp
 output file:  app
 size of exe:  16K bytes

The test code I used was a simple cout:

#include <iostream>

using namespace std;

int main (void) {
    cout << "Code Running" << endl << "and being timed." << endl;
    return 0;
}

Incorporating iowaitnotify

Understanding better what you are attempting to accomplish, below is an addition (without all the validation checks) that will watch your source file and recompile on any change. It also avoids the use of eval. There are several ways to accomplish this, this being one. You can add the validation checks from the script above if you desire:

#!/bin/bash

bldcmd=( ${@:2} )   ## array for build command

while inotifywait -q -e close_write -e delete $1; do 
    echo -e "\ncompiling:  ${bldcmd[@]}"
    # eval "${@:2}"
    ${bldcmd[@]}
    time ./app
    printf "\n app size: %s\n" $(du -h app | awk '{print $1}')
done

Output

$ ./watch . g++ app.cpp -o app
./ CLOSE_WRITE,CLOSE app.cpp

compiling:  g++ app.cpp -o app

Code Running (ver. 6)
and being timed.

real    0m0.003s
user    0m0.001s
sys     0m0.002s

 app size: 16K
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Thank you for big reply. Your script, as I understand, dont look at changes like inotifywait. And there is small error ` line 24: unexpected EOF while looking for matching `)'`. And what mean `store disk usage (removing name)`? – Alex Ushakov Aug 18 '15 at 08:36
  • Sorry, the missing `)` was the result of an accidental backspace. I've also added a way to use an array to avoid `eval` in the answer. – David C. Rankin Aug 18 '15 at 22:32
0

I'm not sure I've read the question correctly, but I think you're asking how to watch a pipeline, i.e. repeatedly run g++ app.cpp -o app && time ./app && du app. If that's what you need to do, you must quote && so it's not interpreted by your (top-level) shell, but instead by the shell that watch invokes:

watch g++ app.cpp -o app '&&' time ./app '&&' du app

You could of course use backslash escaping (\&\& instead of '&&') if you prefer.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
-1

Solution is eval command .

Alex Ushakov
  • 2,139
  • 2
  • 11
  • 8
  • glad you found it, but be aware it can have side-effects. I don't think they are applicable here. – David C. Rankin Aug 18 '15 at 08:13
  • Can you explain - what side effects do you mean? – Alex Ushakov Aug 18 '15 at 08:17
  • Sure see [**Why should eval be avoided in Bash, and what should I use instead?**](http://stackoverflow.com/questions/17529220/why-should-eval-be-avoided-in-bash-and-what-should-i-use-instead) – David C. Rankin Aug 18 '15 at 08:20
  • Please consider editing your answer to include more information (e.g. an example of *how* you used the `eval` command). – Matt Aug 18 '15 at 21:24