引数"--"(Double dash)のみとは何を意味するのか


Bash - [小ネタ]perlワンライナーにシェル変数を渡す - Qiita
http://qiita.com/joemphilips/items/45060735942a36140ad9

```bash
--以降で変数の中身を指定していますが、これはシェル一般に共通する記法ではなく、特殊な記法だと思われます。
```

# getoptにおける引数解析の終了を意味する

[Man page of GETOPT](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/getopt.3.html)
>"--" は特殊な引き数で、スキャンのモードによらず、 オプションのスキャンを強制的に終了させる。

大抵のプログラムは自力で引数解析をする必要がないので内部でgetoptを使用していることが多いと思います。製作者が"--"(Double dash)を意図しているかどうかに関わらず、仕組み上必要な機能です。

なお、getoptの仕組み（仕様）を使わないプログラムはこの影響を受けません。後述。

一般的かどうかについても後述。


# 使用例

通常引数に`-`や`--`をつけるとそれはコマンドに対するオプション（処理の変更）を意味します。
例えば`-`や`--`付きのファイルを削除しようとしてもファイル名をオプションと勘違いして期待通りに動きません。
そこでファイル名の前に`--`を入れるとオプション解析はそこまでで終了だということが伝わり、操作できるようになります。


```bash
$ touch -- -suzaki-nishi
$ ls
-suzaki-nishi
$ rm -suzaki-nishi
rm: 無効なオプション -- 's'
Try 'rm ./-suzaki-nishi' to remove the file `-suzaki-nishi'.
Try 'rm --help' for more information.
$ rm -- -suzaki-nishi
```

# 各実装

## glibc

gccのC言語で作成されているプログラムで`#include <unistd.h>`で呼び出せるgetopt関数の実装

```c:glibc-2.17/posix/getopt.c
      /* The special ARGV-element `--' means premature end of options.
   Skip it like a null option,
   then exchange with previous non-options as if it were an option,
   then skip everything else like a non-option.  */

      if (d->optind != argc && !strcmp (argv[d->optind], "--"))
  {
    d->optind++;

    if (d->__first_nonopt != d->__last_nonopt
        && d->__last_nonopt != d->optind)
      exchange ((char **) argv, d);
    else if (d->__first_nonopt == d->__last_nonopt)
      d->__first_nonopt = d->optind;
    d->__last_nonopt = argc;

    d->optind = argc;
  }
```

## bash

bashのgetopt（シェルで呼び出せるgetoptコマンド）の実装

```c:bash-4.2/builtins/getopt.c
      /* Special ARGV-element `--' means premature end of options.
   Skip it like a null option, and return EOF. */
      if (temp[0] == '-' && temp[1] == '-' && temp[2] == '\0')
  {
    sh_optind++;
    return EOF;
  }

```

## Perl Getopt::Long

標準モジュールGetopt::Longの実装

```pl:Getopt/Long.pm
sub GetOptionsFromArray(@) {

    my ($argv, @optionlist) = @_; # local copy of the option descriptions
    my $argend = '--';    # option list terminator
...
  # Double dash is option list terminator.
  if ( defined($opt) && $opt eq $argend ) {
    push (@ret, $argend) if $passthrough;
    last;
  }

```

# getoptを使わない場合

`--`あろうがなかろうが関係ありません。作り方次第です。

```pl:suzaki-nishi.pl
use strict;
use warnings;
use v5.16;

foreach my $opt (@ARGV) {
        if ($opt eq "-s") {
                say "洲崎";
        }
        if ($opt eq "-n") {
                say "西";
        }
        if ($opt eq "-l") {
                say "SUPER LIVE";
        }
        if ($opt eq "--date") {
                say "2015.12.6";
        }
        if ($opt eq "--place") {
                say "SONIC CITY HALL";
        }
        if ($opt eq "--kiss") {
                say "business kiss";
        }
}
```

```bash
$ perl suzaki-nishi.pl -s -n -l --date --place -- --kiss
洲崎
西
SUPER LIVE
2015.12.6
SONIC CITY HALL
business kiss
```

# "--"(Double dash)はシェルで一般的か？

シェルで一般的かどうかは、bashやcoreutilsに含まれるコマンドがgetoptを採用しているかどうかとなります。coreutilsのコマンドがgetoptを使用しているか調べてみました。

```pl:find_getopt.pl
use strict;
use warnings;
use v5.16;

my $use_getopt = 0;
my $total      = 0;
foreach my $file (`ls src`) {
        chomp $file;
        next unless $file =~ /\.c$/;

        `grep getopt src/$file`;
        #say "[$?] $file";
        $use_getopt++ unless $?;
        $total++;
}
say "$use_getopt/$total";
```

```bash
$ perl find_getopt.pl
 93/118
```

２ファイル以上のコマンドもあるのでそれを除いてもほぼ過半数でした。
なお、getoptを使用していないコマンドは以下の４つでした。
ほぼ単一の動作のみをするものですね。

- echo
- false
- test
- true