Tệp hàng loạt - số lượng đối số dòng lệnh


99

Chỉ cần chuyển đổi một số tập lệnh shell thành các tệp hàng loạt và có một điều tôi dường như không thể tìm thấy ... và đó là một phép đếm đơn giản về số lượng đối số dòng lệnh.

ví dụ. nếu bạn có:

myapp foo bar

Trong vỏ bọc:

  • $ # -> 2
  • $ * -> thanh foo
  • $ 0 -> myapp
  • $ 1 -> foo
  • $ 2 -> thanh

Trong lô

  • ?? -> 2 <---- lệnh gì ?!
  • % * -> thanh foo
  • % 0 -> myapp
  • % 1 -> foo
  • % 2 -> thanh

Vì vậy, tôi đã nhìn xung quanh, và tôi đang nhìn nhầm chỗ hoặc tôi bị mù, nhưng dường như tôi không thể tìm ra cách để có được số lượng các đối số dòng lệnh được truyền vào.

Có một lệnh tương tự như "$ #" của shell cho các tệp hàng loạt không?

ps. gần nhất tôi đã tìm thấy là lặp qua% 1s và sử dụng 'shift', nhưng tôi cần phải tham chiếu% 1,% 2, v.v. sau đó trong tập lệnh nên điều đó không tốt.


chuỗi của bạn là 2 myapp foo bar?
PsychoData

2
lời khuyên: không chuyển đổi sh thành BAT. thay vào đó, hãy tải xuống cygwin và sử dụng nó thay thế. tức là các tập lệnh sh của bạn sẽ hoạt động trên máy windows. và bạn sẽ không phải dịch mọi sh sang BAT!
Marty McGowan

Câu trả lời:


104

Googling một chút cung cấp cho bạn kết quả sau từ wikibooks :

set argC=0
for %%x in (%*) do Set /A argC+=1

echo %argC%

Có vẻ như cmd.exe đã phát triển một chút so với những ngày DOS cũ :)


7
Lưu ý rằng biến thể này forchỉ hoạt động đối với các đối số giống như tên tệp, không phải các chuỗi tùy chọn chẳng hạn như -?. Sử dụng dấu ngoặc kép ( for %%i in ("%*") ...) hoạt động cho đối số như -?nhưng lại không thành công đối với các đối số được trích dẫn do các dấu ngoặc kép lồng nhau. Cách duy nhất mạnh mẽ dường như liên quan đến shift...
Ferdinand Beyer

1
MyBatchFile "*.*" "Abc"báo cáo rằng argC == 9. - Phản đối.
BrainSlugs83

Đã thử nghiệm điều này với 2500 đối số dưới dạng một hàm và sử dụng nó để xác định các mảng có cùng kích thước. Đừng hỏi tôi tại sao chính xác. Chủ yếu là chỉ học những gì lô có khả năng.
T3RR0R

44

Bạn có xu hướng xử lý số lượng đối số với loại logic này:

IF "%1"=="" GOTO HAVE_0
IF "%2"=="" GOTO HAVE_1
IF "%3"=="" GOTO HAVE_2

Vân vân.

Tuy nhiên, nếu bạn có nhiều hơn 9 đối số thì bạn đã gặp khó khăn với cách tiếp cận này. Có rất nhiều cách hack để tạo quầy mà bạn có thể tìm thấy ở đây , nhưng được cảnh báo rằng chúng không dành cho những người yếu tim.


6
Bạn vẫn có thể sử dụng shiftđể đếm hơn 9 ... và không cần có 10 dòng mã giống nhau.
Joey

19

Chức năng :getargcbên dưới có thể là những gì bạn đang tìm kiếm.

@echo off
setlocal enableextensions enabledelayedexpansion
call :getargc argc %*
echo Count is %argc%
echo Args are %*
endlocal
goto :eof

:getargc
    set getargc_v0=%1
    set /a "%getargc_v0% = 0"
:getargc_l0
    if not x%2x==xx (
        shift
        set /a "%getargc_v0% = %getargc_v0% + 1"
        goto :getargc_l0
    )
    set getargc_v0=
    goto :eof

Về cơ bản, nó lặp lại một lần trên danh sách (là cục bộ của hàm nên các thay đổi sẽ không ảnh hưởng đến danh sách trở lại chương trình chính), đếm chúng cho đến khi hết.

Nó cũng sử dụng một thủ thuật tiện lợi, chuyển tên của biến trả về sẽ được đặt bởi hàm.

Chương trình chính chỉ minh họa cách gọi nó và lặp lại các đối số sau đó để đảm bảo rằng chúng không bị ảnh hưởng:

C:\Here> xx.cmd 1 2 3 4 5
    Count is 5
    Args are 1 2 3 4 5
C:\Here> xx.cmd 1 2 3 4 5 6 7 8 9 10 11
    Count is 11
    Args are 1 2 3 4 5 6 7 8 9 10 11
C:\Here> xx.cmd 1
    Count is 1
    Args are 1
C:\Here> xx.cmd
    Count is 0
    Args are
C:\Here> xx.cmd 1 2 "3 4 5"
    Count is 3
    Args are 1 2 "3 4 5"

làm thế nào bạn có thể thay đổi luồng dựa trên điều này? (có thể là một câu hỏi khác nhau, nhưng tôi nghĩ một ví dụ ngắn sẽ cũng rất thuận tiện ở đây!)
n611x007

@ n611x007 trong đó echo Count is %argc%bạn sẽ chèn mã của riêng mình để có thể kiểm tra xem số lượng đó có phù hợp không và sau đó tiếp tục.
kenny

7

Thử cái này:

SET /A ARGS_COUNT=0    
FOR %%A in (%*) DO SET /A ARGS_COUNT+=1    
ECHO %ARGS_COUNT%

6
câu trả lời này bằng cách nào đó khác với câu trả lời @nimrod ? ...
xanh lam

1
kiểm tra nhận xét của @FerdinandBeyer trong nimrodm's. không sẽ downvote bởi vì bạn có 21 đại diện 8)
n611x007

6

Nếu số đối số phải là một số chính xác (nhỏ hơn hoặc bằng 9), thì đây là một cách đơn giản để kiểm tra nó:

if "%2" == "" goto args_count_wrong
if "%3" == "" goto args_count_ok

:args_count_wrong
echo I need exactly two command line arguments
exit /b 1

:args_count_ok

2

Tránh sử dụng một trong hai shifthoặc một forchu kỳ với chi phí kích thước và khả năng đọc.

@echo off
setlocal EnableExtensions EnableDelayedExpansion
set /a arg_idx=1
set "curr_arg_value="
:loop1
if !arg_idx! GTR 9 goto :done
set curr_arg_label=%%!arg_idx!
call :get_value curr_arg_value !curr_arg_label!
if defined curr_arg_value (
  echo/!curr_arg_label!: !curr_arg_value!
  set /a arg_idx+=1
  goto :loop1
)
:done
set /a cnt=!arg_idx!-1
echo/argument count: !cnt!
endlocal
goto :eof

:get_value
(
  set %1=%2
)

Đầu ra:

count_cmdline_args.bat testing more_testing arg3 another_arg

%1: testing
%2: more_testing
%3: arg3
%4: another_arg
argument count: 4

CHỈNH SỬA: "Thủ thuật" được sử dụng ở đây bao gồm:

  1. Xây dựng một chuỗi đại diện cho một biến đối số dòng lệnh hiện được đánh giá (tức là "% 1", "% 2", v.v.) bằng cách sử dụng một chuỗi có chứa ký tự phần trăm ( %%) và một biến đếm arg_idxtrên mỗi lần lặp vòng lặp.

  2. Lưu trữ chuỗi đó vào một biến curr_arg_label.

  3. Truyền cả chuỗi ( !curr_arg_label!) và tên của biến trả về ( curr_arg_value) vào một chương trình con nguyên thủy get_value.

  4. Trong chương trình con, %1giá trị ( ) của đối số đầu tiên của nó được sử dụng ở bên trái của phép gán ( set) và %2giá trị ( ) của đối số thứ hai ở bên phải. Tuy nhiên, khi đối số của chương trình con thứ hai được truyền, nó được trình thông dịch lệnh phân giải thành giá trị của đối số dòng lệnh của chương trình chính. Có nghĩa là, những gì được truyền vào không phải, ví dụ, "% 4" mà là bất kỳ giá trị nào mà biến đối số dòng lệnh thứ tư nắm giữ ("another_arg" trong cách sử dụng mẫu).

  5. Sau đó, biến được cung cấp cho chương trình con dưới dạng biến trả về ( curr_arg_value) được kiểm tra là không xác định, điều này sẽ xảy ra nếu không có đối số dòng lệnh được đánh giá hiện tại. Ban đầu, đây là sự so sánh giá trị của biến trả về được bao bọc trong dấu ngoặc vuông với dấu ngoặc vuông trống (đó là cách duy nhất tôi biết về thử nghiệm đối số chương trình hoặc chương trình con có thể chứa dấu ngoặc kép và là phần còn lại bị bỏ sót từ giai đoạn thử nghiệm và sai sót) nhưng đã được sửa thành như thế nào bây giờ.


1
Mặc dù mã này có thể trả lời câu hỏi, nhưng việc cung cấp thêm ngữ cảnh về cách và / hoặc lý do tại sao nó giải quyết vấn đề sẽ cải thiện giá trị lâu dài của câu trả lời.
thewaywewere

@thewaywewere cảm ơn bạn, tôi quên rằng đối với nhiều người (có thể là đa số) tốt hơn nên có một mô tả văn bản thay vì mã "tự giải thích".
fen-fire

@ fen-fire ngoại trừ việc mã không tự giải thích.
Loduwijk

1

Câu trả lời cuối cùng là cách đây hai năm, nhưng tôi cần một phiên bản cho hơn chín đối số dòng lệnh. Có thể là một người khác cũng làm ...

@echo off
setlocal

set argc_=1
set arg0_=%0
set argv_=

:_LOOP
set arg_=%1
if defined arg_ (
  set arg%argc_%_=%1
  set argv_=%argv_% %1
  set /a argc_+=1
  shift
  goto _LOOP
)
::dont count arg0
set /a argc_-=1
echo %argc_% arg(s)

for /L %%i in (0,1,%argc_%) do (
  call :_SHOW_ARG arg%%i_ %%arg%%i_%%
)

echo converted to local args
call :_LIST_ARGS %argv_%
exit /b


:_LIST_ARGS
setlocal
set argc_=0
echo arg0=%0

:_LOOP_LIST_ARGS
set arg_=%1
if not defined arg_ exit /b
set /a argc_+=1
call :_SHOW_ARG arg%argc_% %1
shift
goto _LOOP_LIST_ARGS


:_SHOW_ARG
echo %1=%2
exit /b

Giải pháp là 19 dòng đầu tiên và chuyển đổi tất cả các đối số thành các biến theo kiểu c-like. Tất cả những thứ khác chỉ thăm dò kết quả và hiển thị chuyển đổi thành các args cục bộ. Bạn có thể tham chiếu các đối số theo chỉ mục trong bất kỳ hàm nào.


0

Một giải pháp mạnh mẽ là ủy thác việc đếm cho một chương trình con được gọi bằng call; chương trình con sử dụng các gotocâu lệnh để mô phỏng một vòng lặp trong đó shiftđược sử dụng để sử dụng lặp đi lặp lại các đối số (chỉ chương trình con):

@echo off
setlocal

:: Call the argument-counting subroutine with all arguments received,
:: without interfering with the ability to reference the arguments
:: with %1, ... later.
call :count_args %*

:: Print the result.
echo %ReturnValue% argument(s) received.

:: Exit the batch file.
exit /b

:: Subroutine that counts the arguments given.
:: Returns the count in %ReturnValue%
:count_args
  set /a ReturnValue = 0
  :count_args_for

    if %1.==. goto :eof

    set /a ReturnValue += 1

    shift
  goto count_args_for
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.