\sys_get_shell:nnN 似乎从 shell 输出中删除换行符

\sys_get_shell:nnN 似乎从 shell 输出中删除换行符

目标:运行 shell 命令并将\input其输出为 LaTeX 代码。

相关内容:如何使用反斜杠保护 \input shell 命令参数

使用 expl3 时\sys_get_shell:nnN,shell 输出中的换行符似乎被替换为空格。
使用某些命令(例如)时会出现此问题\begin{lstlisting},这些命令会丢弃该行中换行符之前的所有文本,然后处理后续行中直到的所有文本\end{lstlisting}

例如,以下内容会产生警告并忽略BOOBAH

\documentclass[11pt]{article}
\usepackage{listings}

\begin{document}
\begin{lstlisting}[breaklines]BAH
Failed to run:
\end{lstlisting}
\begin{lstlisting}[breaklines]BOO
-bash: notACommand: command not found
\end{lstlisting}

\end{document}

警告:

Package Listings Warning: Text dropped after begin of listing on input line 5.
Package Listings Warning: Text dropped after begin of listing on input line 8.

输出:

Failed to run:
-bash: notACommand: command not found

以下命令\input将按预期工作,包括来自 shell 输出的换行符:

\documentclass[11pt]{article}
\usepackage{listings}

\begin{document}

\input{"|OUTPUT=`notACommand \\2\\\{\\3\\( 2>&1` && echo $OUTPUT || ( ( echo \\\\\\\\begin\\{lstlisting\\}\\[breaklines\\] && echo Failed to run: && echo \\\\\\\\end\\{lstlisting\\} ) && ( echo \\\\\\\\begin\\{lstlisting\\}\\[breaklines\\] && echo $OUTPUT && echo \\\\\\\\end\\{lstlisting\\} ) )"}

\end{document}

产生所需的输出:

Failed to run:
sh: notACommand: command not found

但是,看似等效的用法\sys_get_shell:nnN将 shell 输出中的换行符转换为空格:

\documentclass[11pt]{article}
\usepackage{listings}

\begin{document}

\ExplSyntaxOn
\tl_new:N \someVarForShellOutput % declare this variable
\cs_new_protected:Nn \inputFromShellCall:n {
  \sys_get_shell:nnN { #1 } { \ExplSyntaxOff } \someVarForShellOutput % disable expl syntax when parsing shell output
  \tl_analysis_show:N \someVarForShellOutput % for debugging - we see that the newlines from shell output are now spaces
  \tl_use:N \someVarForShellOutput
}
\cs_generate_variant:Nn \inputFromShellCall:n { e } % Create \inputFromShellCall:e which expands its argument before invoking the shell
\ExplSyntaxOff

\ExplSyntaxOn
\inputFromShellCall:e { OUTPUT=`notACommand~ \c_backslash_str 2 \c_backslash_str \{ \c_backslash_str 3 \c_backslash_str (~2>&1`~&&~echo~$OUTPUT~||~(~(~echo~\c_backslash_str \c_backslash_str \c_backslash_str \c_backslash_str begin\{lstlisting\} \c_backslash_str [breaklines\c_backslash_str ]~&&~echo~Failed~to~run:~&&~echo~\c_backslash_str \c_backslash_str \c_backslash_str \c_backslash_str end\{lstlisting\}~)~&&~(~echo~\c_backslash_str \c_backslash_str \c_backslash_str \c_backslash_str begin\{lstlisting\}\c_backslash_str [breaklines\c_backslash_str ]~&&~echo~$OUTPUT~&&~echo~\c_backslash_str \c_backslash_str \c_backslash_str \c_backslash_str end\{lstlisting\}~)~) }
\ExplSyntaxOff

\end{document}

调试\tl_analysis_show:N发现shell输出为:

(|OUTPUT=`notACommand \2\\{\3\( 2>&1` && echo $OUTPUT || ( ( echo \\\\begin\{lstlisting\}\[breaklines\] && echo Failed to run: && echo \\\\end\{lstlisting\} ) && ( echo \\\\begin\{lstlisting\}\[breaklines\] && echo $OUTPUT && echo \\\\end\{lstlisting\} ) ))
The token list \someVarForShellOutput contains the tokens:
>  \begin (control sequence=macro:->\protect \begin  )
>  { (begin-group character {)
>  l (the letter l)
>  s (the letter s)
>  t (the letter t)
>  l (the letter l)
>  i (the letter i)
>  s (the letter s)
>  t (the letter t)
>  i (the letter i)
>  n (the letter n)
>  g (the letter g)
>  } (end-group character })
>  [ (the character [)
>  b (the letter b)
>  r (the letter r)
>  e (the letter e)
>  a (the letter a)
>  k (the letter k)
>  l (the letter l)
>  i (the letter i)
>  n (the letter n)
>  e (the letter e)
>  s (the letter s)
>  ] (the character ])
>    (blank space  )
>  F (the letter F)
>  a (the letter a)
>  i (the letter i)
>  l (the letter l)
>  e (the letter e)
>  d (the letter d)
...

即使手动运行 shell 命令

OUTPUT=`notACommand \2\\{\3\( 2>&1` && echo $OUTPUT || ( ( echo \\\\begin\{lstlisting\}\[breaklines\] && echo Failed to run: && echo \\\\end\{lstlisting\} ) && ( echo \\\\begin\{lstlisting\}\[breaklines\] && echo $OUTPUT && echo \\\\end\{lstlisting\} ) )

在“失败”之前生成带有换行符的输出:

\begin{lstlisting}[breaklines]
Failed to run:
\end{lstlisting}
\begin{lstlisting}[breaklines]
sh: notACommand: command not found
\end{lstlisting}

https://mirror.mwt.me/ctan/macros/latex/contrib/l3kernel/interface3.pdf对于\sys_get_shell:nnN各州:

The ⟨shell command⟩ is converted to a string using \tl_to_str:n. Category codes may need to be set appropriately via the ⟨setup⟩ argument, which is run just before running the ⟨shell command⟩ (in a group).

\tl_to_str:n听起来它适用于 shell 的输入,但不适用于输出,因此与此无关。
这里的类别代码设置似乎应该没问题 - 我关闭了 expl3 语法,因为它lstlisting似乎在内部不起作用\ExplSyntaxOn(我还没有调查原因)。

编辑:这也可以重现,其中 shell 调用站点不在expl3语法内,因此不需要设置类别代码:

\documentclass[11pt]{article}
\usepackage{listings}

\begin{document}

\ExplSyntaxOn
\tl_new:N \someVarForShellOutput % declare this variable
\cs_new_protected:Nn \inputFromShellCall:n {
  \sys_get_shell:nnN { #1 } { } \someVarForShellOutput % disable expl syntax when parsing shell output
  \tl_analysis_show:N \someVarForShellOutput % for debugging - we see that the newlines from shell output are now spaces
  \tl_use:N \someVarForShellOutput
}
\cs_generate_variant:Nn \inputFromShellCall:n { e } % Create \inputFromShellCall:e which expands its argument before invoking the shell

\NewDocumentCommand{\simpleNameInputFromShellCall}{m} {
  \inputFromShellCall:e { #1 }
}
\ExplSyntaxOff

\simpleNameInputFromShellCall{ OUTPUT=`notACommand \\2\\\{\\3\\( 2>&1` && echo $OUTPUT || ( ( echo \\\\begin\{lstlisting\}\\[breaklines\\] && echo Failed to run: && echo \\\\end\{lstlisting\} ) && ( echo \\\\begin\{lstlisting\}\\[breaklines\\] && echo $OUTPUT && echo \\\\end\{lstlisting\} ) ) }

\end{document}

为什么 shell 输出中的换行符会被变成空格\sys_get_shell:nnN,并且可以防止这种情况发生?

第二次编辑:

如何将 shell 输出保存到 LaTeX 中的变量?讨论了如何\read读取平衡行并将换行符转换为空格。
它说“为了避免这种情况,请将其设置\endlinechar为 -1。”,但这会将换行符转换为 <nothing>。

\endlinechar\sys_get_shell:nnN设置参数中将某个可见的字符设置为\endlinechar=97(即a)可成功生成as 而不是换行符。但\endlinechar=10(换行符的实际 ascii 代码)实际上会在 shell 输出中插入换行符,但似乎具有错误的类别代码,导致 listlisting 无法识别它。


\sys_get_shell:nnN { #1 } { \endlinechar=10 \catcode\endlinechar=5 } \someVarForShellOutput
我还尝试在将 newlinechar: 设置为从 1 到 16 的所有内容后手动修改 catcode ,但没有成功。
将其设置为 5 会导致换行符被空格替换 - 没有任何 shell 设置代码时会出现相同的行为,这是有道理的,因为它的默认 catcode(EXPL 之外)似乎是 5: \showthe\catcode\endlinechar

这种语言很痛苦,哈哈,我看不到其他解决方法,除了跳过\sys_get_shell:nnN并改用\input{"|..."}

答案1

从根本上来说,您仍然需要了解 TeX 中的 catcode 如何工作,才能理解 expl3 函数的作用(TeXbook/TeX by topic 可以提供帮助)。

无论如何,逐字逐句的环境(例如)listings在读取内容时会更改 catcode,而 expl3sys_get_shell:n会在读取后立即对整个内容进行标记,这使得无法保证的正确行为listings

(*) 如果您确切知道需要哪些 catcode,listings并设置了这些精确的 catcode,那么这也许是可行的;尽管如此,这种实现在很大程度上取决于listings环境的内部实现。

无论如何,一个解决方法是让生的内容并sys_get_shell用它标记scantokens(目前没有 expl3 包装器(\tl_rescan并且变体在开始时将整个内容标记化,由于上述相同的原因,这在这里不适用),除此之外根本\tex_scantokens:D不是“包装器”)

\documentclass{article}
\usepackage{listings}


\begin{filecontents*}[overwrite]{test.txt}
\begin{lstlisting}
line 1  !?%#\xyz
line 2
\end{lstlisting}
\end{filecontents*}

\begin{document}

\ExplSyntaxOn
\tl_new:N \someVarForShellOutput % declare this variable
\cs_new_protected:Nn \inputFromShellCall:n {
    \sys_get_shell:nnN { #1 } { \cctab_select:N \c_other_cctab \endlinechar=10~ } \someVarForShellOutput
    \scantokens \expandafter { \someVarForShellOutput }
}
\cs_generate_variant:Nn \inputFromShellCall:n { e } % Create \inputFromShellCall:e which expands its argument before invoking the shell

\NewDocumentCommand{\simpleNameInputFromShellCall}{m} {
  \inputFromShellCall:e { #1 }
}
\ExplSyntaxOff

\simpleNameInputFromShellCall{ cat test.txt }

\end{document}

參閱我怎样才能将整个文件内容逐字逐句地读入(expl3)字符串变量中?用于解释 catcode 更改部分。

相关内容