在路径或文件名中包含 ö、ä、ü 时,'findstr /b "URL=" "%~1"' 命令无法正常工作。

4

我想在一个文件中搜索“URL=”。由于我对这方面的知识很菜,所以我从stackoverflow收集了一些代码片段……;-)

http://www.dostips.com/forum/viewtopic.php?f=3&t=2836&start=30

获取Windows批处理脚本(.bat)中传递参数的列表

如何接收最奇怪的命令行参数?

我的问题: 如果文件或路径包含德语的“ö/ä/ü”或其他外语中的字母/符号在路径或文件名中

D:\...\fähren

被处理成这样

D:\...\f"hren

findstr提示“无法打开文件”。这是我的.bat文件的一部分:

rem %cmdcmdline%
...
:file   rem url from .url file  - im file steht URL=http.... .htm
for /f "delims=" %%a in ('findstr /b "URL=" "%~1"') do set URL="%%a"
echo. %URL% | FIND /I "URL=">Nul || (set URL=""&goto startit)

rem delete all until URL
set URL="%URL:*URL=%
rem delete =
set URL="%URL:~2%

这个 .bat 文件是在 Windows 中这样调用的

HKEY_CLASSES_ROOT\InternetShortcut\shell\chrome\command "D:\sources\chrome\portable\chrome\chrome.exe" "%1"

.bat文件开头加上“rem %cmdcmdline%”看起来很好

D:\4all\reisen\istanbul\verkehr\fähren>rem C:\Windows\system32\cmd.exe /c ""C:\Users\gigoelri\AppData\Local\Temp\333A.tmp\chrome_pause.bat" D:\sources\chrome\portable\chrome\chrome.exe D:\4all\reisen\istanbul\verkehr\fähren\Bosp_eminönü_2h_14h30_12tl_SehirHatlari.url "

for循环的输出如下:
D:\4all\reisen\istanbul\verkehr\fähren>for /F "delims=" %a in ('findstr /b "URL=" "D:\4all\reisen\istanbul\verkehr\fähren\Bosp_eminönü_2h_14h30_12tl_SehirHatlari.url"') do set URL="%a"
FINDSTR: D:\4all\reisen\istanbul\verkehr\f"hren\Bosp_emin"n?_2h_14h30_12tl_SehirHatlari.url kann nicht geöffnet werden.(cannot be opened)

D:\4all\reisen\istanbul\verkehr\fähren>echo.    | FIND /I "URL="  1>Nul  || (set URL=""  & goto startit )

我的cmd窗口的代码页:

D:\sources\firefox\_install>chcp
Aktive Codepage: 850.

Mofi 4:尝试使用Mofi的方法4 - 结果:找不到文件

rem C:\Windows\system32\cmd.exe /c ""C:\Users\gigoelri\AppData\Local\Temp\F54D.tmp\firefox_pause.bat" D:\sources\firefox\portable\firefox\firefox.exe D:\4all\reisen\istanbul\verkehr\fähren\Bosp_eminönü_2h_14h30_12tl_SehirHatlari.url "
...
D:\4all\reisen\istanbul\verkehr\fähren>for /F "usebackq tokens=1* delims==" %a in ("D:\4all\reisen\istanbul\verkehr\fähren\Bosp_eminönü_2h_14h30_12tl_SehirHatlari.url") do (if /I "%a" == "URL" (
set "URL=%b"
 goto startit
) )
Die Datei "D:\4all\reisen\istanbul\verkehr\fähren\Bosp_eminönü_2h_14h30_12tl_SehirHatlari.url" 
kann nicht gefunden werden.

这次的原因似乎是文件名包含土耳其字母,比如“Ş”,而不是“S”。
编辑于20150629:
系统是Windows7,驱动器D:是NTFS。
%~s1 也无效:
D:\4all\reisen\istanbul\verkehr\fähren>for /F "usebackq tokens=1* delims==" %a in ("D:\4all\reisen\istanbul\verkehr\FHREN~1\Bosp_eminönü_2h_14h30_12tl_SehirHatlari.url") do (if /I "%a" == "URL" (
set "URL=%b"
 goto startit
) )
Die Datei "D:\4all\reisen\istanbul\verkehr\FHREN~1\Bosp_eminönü_2h_14h30_12tl_SehirHatlari.url" kann nicht gefunden werden.

文件名为:Bosp_eminönü_2h_14h30_12tl_ŞehirHatları.url的资源管理器文件 该URL文件是通过从Chrome中拖放以下URL创建的: URL: http://en.sehirhatlari.com.tr/en/timetable/short-bosphorus-tour-363.html

%windir%\system32\cmd.exe dir命令显示: 结尾的Ş和ı都无法正常显示。

而且,.exe似乎已经以错误的名称被调用了:

编辑20150630a:

我使用Bat_To_Exe_Converter_(x64).exe将.bat转换为.exe。我这样做是因为这样可以保持注册表条目不变,并且可以轻松固定.exe。

你是对的,如果windows调用.bat,一切正常。

HKEY_CLASSES_ROOT\IE.AssocFile.URL\Shell\firefox\command
"D:\sources\firefox\_install\firefox.bat" "%1"

是否可以这样说,无论是调用.bat还是.exe文件,Windows在传递参数时是否有所不同?

!!!! @Mofi:感谢您的扩展1a支持 !!!!

这似乎不是“bat到exe转换器”的问题,因为: 请看第一行rem语句。 它与在20150629编辑中发布的.exe截图相差很大。 有一个额外的语句“C:\ Users \ gigoelri \ AppData \ Local \ Temp \ F411.tmp \ firefox_pause.bat”,“”设置不同,并且URL末尾的拼写不同 ... ı .url“”而不是... i .url“


尝试使用短名称"%~s1" - npocmaka
1个回答

4

1. 关于引用赋值给变量的值

一个非常常见的错误是使用:

set variable="value with spaces"

这段代码将"value with spaces"和行末的所有内容(包括尾随空格)分配给variable变量。
第一个双引号的正确位置是:
set "variable=value with spaces"

这将仅仅把带有空格的value赋给变量variable,与此行末尾的空格或制表符无关。
更多细节请参见我在命令行中使用“set var = text”后为什么没有字符串输出?上的回答。

2. 在FOR循环中测试是否完成了赋值

for /f "delims=" %%a in ('findstr /b "URL=" "%~1"') do set URL="%%a"
echo. %URL% | FIND /I "URL=">Nul || (set URL=""&goto chrome)

这是一种比实际需要更为复杂的测试 FOR 循环中赋值的方法。

更简单易读且执行速度更快的方法是:

@echo off
set "URL="
for /F "delims=" %%a in ('%SystemRoot%\System32\findstr.exe /b "URL=" "%~1" 2^>nul') do set "URL=%%a"

if "%URL%"=="" goto Chrome

rem Remove URL= from string value.
set "URL=%URL:~4%"

echo URL found: %URL%
goto :EOF

:Chrome
echo No URL found.

现在不区分大小写地删除 URL= 变得更加容易了,因为由于引用值赋值给变量 URL,双引号不是字符串值的一部分。

3. GUI和控制台窗口的代码页

在德国等国家,GUI中非Unicode字符串使用的代码页是 Windows-1252

但是在控制台窗口中,默认情况下在德国等国家使用的是 OEM代码页 850

通过比较这两个表格可以看出,在这两个代码页中,德语umlauts具有不同的字节值,这解释了您所看到的内容。

可以通过打开命令提示窗口并在其中运行没有任何参数的命令 chcp 或命令 mode 来查看控制台窗口中默认使用的代码页。在这两种情况下,所使用的代码页会在控制台窗口中输出。

命令 chcp 意味着 更改代码页,因此可用于切换活动命令提示符的代码页。

批处理文件中需要执行的操作取决于传递给批处理文件作为参数的文件名字符串使用的编码方式。


在提供有关如何调用批处理文件的其他信息后进行编辑。

4. 不使用 FINDSTR 的方法

这个任务不需要使用 findstr。使用 findstr 只会使批处理文件比必要的更加缓慢和复杂。

因此,我建议使用一个更简单的批处理解决方案来完成这个任务:

@echo off
for /F "usebackq tokens=1* delims==" %%a in ("%~1") do (
    if /I "%%a"=="URL" (
        set "URL=%%b"
        goto FoundURL
    )
)
echo No URL found.
goto :EOF

:FoundURL
echo URL found: %URL%

现在,*.url文件直接通过命令行解释器使用for而不是使用findstr进行解析。
在命令提示符窗口中运行for /?获取有关此命令的帮助。
默认情况下,使用参数/F时,双引号中的字符串将直接解析。但对于此任务,必须解析指定了完整路径的双引号中的文件。因此,使用usebackq来更改for的字符串解析行为,以获取带有路径的文件名解释为要解析的文件的名称。
接下来,此批处理文件只对一行感兴趣:
URL=https://stackoverflow.com/

所以delims==被用来将每行分隔成以等号为分隔符的字符串。

需要的是第一个等号左边的字符串和第一个等号右边的所有东西,当然也可能包含一个或多个等号。我们可以使用tokens=1*得到这种分割行为。第一个等号左边的字符串是token 1,被赋值给循环变量a;第一个等号右边的所有东西是token 2,被赋值给循环变量b

通过不区分大小写比较第一个等号左边的字符串和字符串URL,检查文件中是否找到了感兴趣的行。在这种情况下,token 2即URL字符串被赋值给环境变量URL,并跳转到标签退出循环,因为没有必要继续解析文件的其余行。

如果for循环正常结束,则*.url文件中没有以URL=开头的行。然后显示适当的信息消息,并使用goto :EOF退出批处理文件(EOF - 文件结尾 - 现在总是存在的预定义标签)。

否则,在退出此演示批处理文件之前,输出找到的URL。

可以在命令提示符窗口中调用此批处理文件。

D:\4all\reisen\istanbul\verkehr\fähren\Bosp_eminönü_2h_14h30_12tl_SehirHatlari.url

或者从Windows资源管理器中打开带有德语umlauts的文件并解析它也没有问题。


提问者 问道:

是不是Windows根据调用.bat还是.exe来传递参数的方式不同?

对于文件和目录名,这是正确的。

"%1" 在文件关联中是一个占位符,通常是文件或目录的名称。

现在Windows有3种将目录或文件名传递给应用程序的可能性:

  1. 使用8.3格式的短格式,用于路径中的所有目录和文件名本身。 8.3表示仅对于目录/文件名最多使用8个字符,并且仅对于具有非常有限字符集的文件扩展名使用3个字符。如果要启动的应用程序(.com或.exe)是16位应用程序,则Windows将使用此格式。

  2. 使用仅限ANSI字符的长格式,即每个字符1个字节,并以null字节结尾。如果应用程序是32位或64位应用程序,则Windows将使用此格式,而不支持Unicode。字符串中存在Unicode字符的目录和文件名将转换为非Unicode感知应用程序的系统语言环境代码页。用户可以在Windows“区域和语言”设置中设置非Unicode感知应用程序的系统语言环境。

  3. 使用Unicode字符的长格式,即如果要启动的应用程序根据其标头是Unicode感知,则每个字符为2个字节。

ANSI字符串在Windows编码应用程序中使用类型为char的数组,而Unicode字符串使用类型为wchar_t的数组。有关Windows的C/C++程序员的详细信息可以在以下地方找到:

在Windows注册表的HKEY_CLASSES_ROOT中,如果Windows应该始终以长格式而不是短格式传递文件或目录名给应用程序,那么可以使用"%L"代替"%1"进行文件关联。有时需要这样做,例如当应用程序是混合型的(如使用DJGPP编译的C/C++控制台应用程序),尽管它包含特殊启动代码,但仍支持长的ANSI编码文件名。
但回到问题本身:当然,根据可执行文件的头部信息,Windows会根据批处理文件或可执行文件的类型以不同的方式传递文件和目录名,也会根据其支持的字符串类型进行传递。
看起来所使用的bat转exe转换器创建了一个64位的控制台应用程序,并且支持Unicode。因此,该应用程序必须使用用户账户的系统区域设置将Unicode字符串正确转换为ANSI字符串,最后将文件、目录名和其他参数传递给嵌入的批处理文件。但似乎这个转换器没有完全正确地完成Unicode到ANSI的转换任务或创建运行批处理文件的命令行。

谢谢您详细的回答!您知道有没有一个转换器可以在这种情况下正确地进行转换吗? - ggggg
我从未使用过任何批处理转可执行文件的工具,所以无法帮助您找到适合您任务的好工具。 - Mofi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接