Extraire la dernière ligne de la string multiligne

La sortie de exif ressemble à ceci:

 ExifMnoteCanon: Loading entry 0xcf27 ('(null)')... ExifMnoteCanon: Loading entry 0x3ca8 ('(null)')... ExifMnoteCanon: Loading entry 0xf88a ('(null)')... 2013:08:22 18:01:16 

Dans mon script bash, je le stocke dans une variable:

 datetime="$(exif --debug --machine-readable --tag=DateTimeOriginal "$file" 2>&1)" 

Je veux extraire la dernière ligne de ceci en utilisant la substitution de paramètre bash. Je pensais que cela fonctionnerait:

 datetime="${datetime##*\n}" 

Mais la sortie est alors:

 ull)')... 2013:08:22 18:01:16 

Pourquoi cela ne marche-t-il pas et comment puis-je le réparer?

Utilisez la séquence d'échappement de style ANSI C, $'\n' pour indiquer la nouvelle ligne:

 % echo "$datetime" ExifMnoteCanon: Loading entry 0xcf27 ('(null)')... ExifMnoteCanon: Loading entry 0x3ca8 ('(null)')... ExifMnoteCanon: Loading entry 0xf88a ('(null)')... 2013:08:22 18:01:16 % echo "${datetime##*\n}" ull)')... 2013:08:22 18:01:16 % echo "${datetime##*$'\n'}" 2013:08:22 18:01:16 

Comme vous pouvez le voir autrement \n est traité comme littéral n .

Bien que $'' soit assez portable ces jours-ci (BSD sh prend en charge si, par exemple, bien que son dash aval ne fonctionne pas) , POSIXLY:

 eval 'printf "%s\n" "${datetime##*"'"$(printf '\n"')}\"" 

… fonctionnera, même si c'est ennuyeux. En général, je garde une nouvelle ligne dans une variable $nl , et ainsi:

 printf "%s\n" "${datetime##*$nl}" 

… est beaucoup plus gérable. Et bien sûr, vous pouvez simplement faire:

 printf "%s\n" "${datetime##*" "}" 

… mais c'est un peu drôle, peut-être.

Alternativement, avec une version récente de bash vous pouvez utiliser mapfile et la substitution de process pour stocker la sortie exif dans un tableau, puis accéder au dernier élément

 mapfile -t arr < <( exif --debug --machine-readable --tag=DateTimeOriginal "$file" 2>&1) printf '%s\n' "${arr[@]:(-1)}" 2013:08:22 18:01:16