find -exec에서 인수로 리터럴 {} 전달 [중복]

find -exec에서 인수로 리터럴 {} 전달 [중복]

for다음과 유사한 중첩 루프가 포함된 Stack Overflow의 질문에 대한 답변을 작성 중입니다.

for jpeg in **/foo.jpg; do
    d=${f%/foo.jpg}   # Directory containing foo.jpg
    for m4a in "$d"/*.m4a; do
        someCommand "$m4a" --arg "$jpeg" 
    done
done

find나는 다음을 사용하여 모든 것을 명령으로 바꾸는 아이디어를 가지고 있었습니다.다른 find다음과 유사한 기본 명령 -exec:

find . -type d -exec test -f {}/foo.jpg \;
               -exec find {} -name '*.m4a' \
                             -exec someCommand ??? --args {}/foo.jpg \;

문제 는 ???.​​​​{}find-execfind{}/foo.jpg

POSIX 표준은 {}리터럴을 명령에 대한 인수로 전달하는 것에 대해 실제로 아무 말도하지 않습니다. 그것은 단지 말한다

Utility_name 또는 인수 문자열에 "{}" 두 문자 대신 "{}" 두 문자가 포함된 경우 find가 해당 두 문자를 대체할지 또는 변경되지 않은 문자열을 사용할지 여부는 구현에 따라 정의됩니다.

이것은 관련된 어떤 종류의 속임수를 배제하는 것 같습니다 sh -c.

-exec sh -c 'someCommand {} --args "$1"' _ {}/foo.jpg \;

왜냐하면 {}외부적으로 교체될 수도 있고 그렇지 않을 수도 있기 때문입니다 find.

find그렇다면 이런 식으로 둥지를 지을 수 있는 방법이 없을까요?

답변1

찾기에는 이스케이프 메커니즘이 없습니다.

{}이 사실은 기존 -exec및/또는 옵션을 삽입하는 것을 허용하지 않습니다 -execdir. 교체는 plain 을 사용하여 수행됩니다 strncpy(). /는 GNU-find( 참고 exec용 으로 사용함) execdir내부로 전달됩니다 bc_push_arg. {} +우리가 가지고 있는 테이블 의 경우 :

  /* "+" terminator, so we can just append our arguments after the
   * command and initial arguments.
   */
  execp->replace_vec = NULL;
  execp->ctl.replace_pat = NULL;
  execp->ctl.rplen = 0;
  execp->ctl.lines_per_exec = 0; /* no limit */
  execp->ctl.args_per_exec = 0; /* no limit */

  /* remember how many arguments there are */
  execp->ctl.initial_argc = (end-start) - 1;

  /* execp->state = xmalloc(sizeof struct buildcmd_state); */
  bc_init_state (&execp->ctl, &execp->state, execp);

  /* Gather the initial arguments.  Skip the {}. */
  for (i=start; i<end-1; ++i)
{
  bc_push_arg (&execp->ctl, &execp->state,
           argv[i], strlen (argv[i])+1,
           NULL, 0,
           1);
}

{}양식에 여러 인스턴스를 가질 수 없기 때문에 모든 것을 끝에 추가합니다 {} +. 우리 에게는 {} ;다음이 있습니다.

  /* Semicolon terminator - more than one {} is supported, so we
   * have to do brace-replacement.
   */
  execp->num_args = end - start;

  execp->ctl.replace_pat = "{}";
  execp->ctl.rplen = strlen (execp->ctl.replace_pat);
  execp->ctl.lines_per_exec = 0; /* no limit */
  execp->ctl.args_per_exec = 0; /* no limit */
  execp->replace_vec = xmalloc (sizeof(char*)*execp->num_args);


  /* execp->state = xmalloc(sizeof(*(execp->state))); */
  bc_init_state (&execp->ctl, &execp->state, execp);

  /* Remember the (pre-replacement) arguments for later. */
  for (i=0; i<execp->num_args; ++i)
{
  execp->replace_vec[i] = argv[i+start];
}

그래서 우리는 execp->ctl.replace_pat = "{}";. 다 거기 있어parser.c

위 방정식을 다음으로 바꾸세요.

  size_t len;               /* Length in ARG before `replace_pat'.  */
  char *s = mbsstr (arg, ctl->replace_pat);
  if (s)
    {
      len = s - arg;
    }
  else
    {
      len = arglen;
    }

  if (bytes_left <= len)
    break;
  else
bytes_left -= len;

  strncpy (p, arg, len);
  p += len;
  arg += len;
  arglen -= len;

에서bc_do_insert()buildcmd.c.

그러니 탈출구는 없습니다 {}. 그러나 일부 find 버전은 대체되지 않고 자체적 {}/foo으로만 대체 {}되므로 두 가지 다른 버전의 find를 와 결합할 수 있습니다 -exec sh -c 'someCommad {}'.

gfindGNU-find 및 AIX find를 가정하면 afind다음을 달성할 수 있습니다.

afind . -type d -execdir test -f foo.jpg \
        -exec sh -c 'gfind . -name "*.m4a" -exec someCommand {} \;' \;

그러나 그것은 끔찍한 해킹이 될 것입니다.

좋은 해결책

당신이 직면하고 있는 문제는 디렉토리에 있는 특정 유형의 모든 파일을 얻기 위해 전역 검색을 수행하고 있다는 것입니다. 즉, 먼저 디렉터리를 찾은 다음 해당 디렉터리의 모든 파일을 와일드카드로 지정하여 디렉터리의 일부로 만듭니다.단일 명령줄.

-execdir이것이 해결되어야 할 문제입니다. 발견된 파일이 포함된 디렉터리에서 명령을 실행합니다. 예를 들어:

$ mkdir -p a/a b/b c/c d e
$ touch a/a/foo.m4a b/b/foo.m4a
$ touch a/a/bar.m4a b/b/bar.m4a c/c/foobar.m4a
$ touch a/a/yay.jpg c/c/yay.jpg
$ find . -type f -name '*.m4a' -execdir test -e yay.jpg \; \
                               -execdir echo someCommand --arg yay.jpg {} +
someCommand --arg yay.jpg ./foobar.m4a
someCommand --arg yay.jpg ./bar.m4a ./foo.m4a

또한 form {} +대신 form을 사용하고 있습니다 . 그러면 발견된 모든 파일(실행된 디렉터리에 있음)이 동일한 명령줄에 배치됩니다.{} ;exec

명령이 차단되었기 b/b때문에 실행되지 않았습니다 .test -e

관련 정보